Create Table from Select with rows as Columns - sql

I need to create a table with columns based on my paramaters. The parameter is string variable and it consists of month and year combinations of multiple values seperated by comma. Based on this values i need to create temp table. I tried like below
DECLARE #compareMonths VARCHAR(2000)
DECLARE #compareCount INT
DECLARE #cols VARCHAR(2000)
DECLARE #query AS NVARCHAR(MAX);
SET #compareMonths = '2014-01,2014-02'
select #cols = STUFF((SELECT distinct ',' +
CONVERT(CHAR(3), DATENAME(MONTH, CAST(
CAST(SUBSTRING(Data, 0, 5) AS VARCHAR(4)) +
RIGHT('0' + CAST(SUBSTRING(Data, 6, 2) AS VARCHAR(2)), 2) +
RIGHT('0' + CAST(1 AS VARCHAR(2)), 2)
AS DATETIME))) + ' ' + CAST(SUBSTRING(Data, 0, 5) AS VARCHAR(4))
FROM dbo.Split(#compareMonths, ',')
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
Here i got the column name as monthname and year but how to create table dynamically based on this. I don't have any value column to use pivot.
My outout table must have 2 columns named Jan 2014, Feb 2014

If you want to a SQL database table, you can build Dynamic SQL script which forms a CREATE TABLE script as follows
DECLARE #compareMonths VARCHAR(2000)
SET #compareMonths = '2014-01,2014-02'
declare #sql nvarchar(max)
SELECT #sql = isnull(#sql + ',','') + ( '[' + val + ']') + ' varchar(100)' from dbo.Split(#compareMonths, ',')
SET #sql = N'CREATE Table OutputTable (' + #sql + ')'
exec sp_executeSql #sql
select * from OutputTable
In above script, I also used a sql string split function where you can find the source codes at the referenced tutorial
At the end you will have a table named OutputTable in your database with two columns

Related

how to create dynamic table

I have a dynamic pivoted query which generates a result set and I want to insert that data into a table. But the problem is columns are dropped or generated by the time. So by the time I cannot predict columns. That is why I created a dynamic pivoted dataset. So how to insert that data set into table?
One solution is to drop and recreate the table every time but I don't know how to do it. I tried CTE, TEMP table but EXEC support only select, insert, update, delete statement:
DECLARE #columns NVARCHAR(MAX), #sqlquery NVARCHAR(MAX), #orderby Nvarchar(MAX),#value Nvarchar(max);
SET #columns = N'';
SET #value=N'0'
SELECT #columns += N', ' + QUOTENAME([Note_Type])
FROM
(
SELECT distinct
No_T
FROM [DS_DM].[dbo].[DAILY_TABLE]
where No_T not in (570,80,150,590,80,99)
)as A order by No_T
SET #sqlquery = N'
Select
K._Number
,D.C_Number
,' + STUFF(#columns, 1, 2, '') + '
from
(
select
_Number
,' + STUFF(#columns, 1, 2, '') + '
from
(
select distinct
right(REPLICATE('+#value+',11) +_Number,11) as [_Number]
,No_t
,No_T_Des
FROM [DS_DM].[dbo].[DAILY_TABLE]
where No_T not in (570,80,150,590,80,99)
)AS J
pivot
(
count(No_T_Des) FOR [No_t] IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
)P
)K
left join
[DS_DM].[dbo].[D_TABLE] D on k._Number = D._Number
';
EXEC sp_executesql #sqlquery
I've modified your code to reflect my proposed solution.
IF OBJECT_ID (N'NEW_TABLE', N'U') IS NOT NULL
BEGIN
DROP TABLE NEW_TABLE
END
DECLARE #columns NVARCHAR(MAX), #sqlquery NVARCHAR(MAX), #orderby Nvarchar(MAX),#value Nvarchar(max);
SET #columns = N'';
SET #value=N'0'
SELECT #columns += N', ' + QUOTENAME([Note_Type])
FROM
(
SELECT distinct
No_T
FROM [DS_DM].[dbo].[DAILY_TABLE]
where No_T not in (570,80,150,590,80,99)
)as A order by No_T
SET #sqlquery = N'
Select
K._Number
,D.C_Number
,' + STUFF(#columns, 1, 2, '') + '
INTO NEW_TABLE
from
(
select
_Number
,' + STUFF(#columns, 1, 2, '') + '
from
(
select distinct
right(REPLICATE('+#value+',11) +_Number,11) as [_Number]
,No_t
,No_T_Des
FROM [DS_DM].[dbo].[DAILY_TABLE]
where No_T not in (570,80,150,590,80,99)
)AS J
pivot
(
count(No_T_Des) FOR [No_t] IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
)P
)K
left join
[DS_DM].[dbo].[D_TABLE] D on k._Number = D._Number
';
EXEC sp_executesql #sqlquery
So I found the answers to my questions.
there are 2 fileds which remais static every time. so I've created ETL that drop that table and recreate every single day with those two fileds. and rest of the 66 columns which are dynamic (drops or newly created) every day. so i've made a cursor for that which loop through all of that categories and altering that table that've created and added that code to ETL
So bassically Every single day that ETL Runs Drop the existing table, Create new table with the 2 static filds and altering same table using cursor with the dynamic filds.

Inserting Different Value IN a Table

Well I'm Creating a Table Based on Result Set Of Databases On Server which contain Multiple Databases.
My table will Create a list of column which contain Database_Name
FOR EG:
DECLARE #strt INT,#End INT,#Database NVARCHAR(20), #ColumnDeclaration VARCHAR(2000),#SqlSelect NVARCHAR(MAX),#column_Name NVARCHAR(255)
SELECT * INTO #T FROM SYS.DATABASES
ORDER BY NAME
SELECT #ColumnDeclaration=STUFF(( SELECT ', ' + Name + ' NVARCHAR(255)'
FROM #T
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(max)'), 1, 1, '')
SET #SqlSelect=' CREATE TABLE Temp_Comp (' + #ColumnDeclaration + ');'
PRINT #SqlSelect
EXEC (#SqlSelect)
SELECT * FROM Temp_Comp
I wan't Insert the Data INTO Temp_Comp Table a specific Value from
the database whose Name will be in Temp_comp Table
Not sure what are you trying to achieve, but I assume that you want something like:
DECLARE #strt INT,#End INT,#Database NVARCHAR(20), #ColumnDeclaration NVARCHAR(MAX),#SqlSelect NVARCHAR(MAX),#column_Name NVARCHAR(255)
SELECT * INTO #T
FROM SYS.DATABASES
ORDER BY NAME;
SELECT #ColumnDeclaration=STUFF(( SELECT ', ' + QUOTENAME(Name) + ' NVARCHAR(255)'
FROM #T
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(max)'), 1, 1, '');
SET #SqlSelect=' CREATE TABLE Temp_Comp (col_desc NVARCHAR(200), ' + #ColumnDeclaration + ');';
SELECT #SqlSelect;
EXEC (#SqlSelect);
SET #SQLSelect = ' INSERT INTO Temp_Comp(col_desc, ' + REPLACE(#ColumnDeclaration, 'NVARCHAR(255)', '') + ')' +
' SELECT col,'+ REPLACE(#ColumnDeclaration, 'NVARCHAR(255)', '') +' FROM (' +
' SELECT name, sub.* FROM sys.databases' +
' OUTER APPLY (SELECT ''database_id'', CAST(database_id AS NVARCHAR(MAX)) UNION ALL SELECT ''compatibility_level'', CAST(compatibility_level AS NVARCHAR(MAX)) ) sub(col, val)) src' +
' PIVOT (MAX(val) FOR name IN ('+ REPLACE(#ColumnDeclaration, 'NVARCHAR(255)', '') +') ) piv';
SELECT #SQLSelect;
EXEC (#SQLSelect);
DBFiddle
Result:
In order to INSERT you could use dynamic PIVOT. To add more values just extend OUTER APPLY part.
Keep in mind that you've defined columns as ' NVARCHAR(255)' so you may need to convert values before insert.

Pivot Rows to Columns Dynamically - SQL

/****** Script for SelectTopNRows command from SSMS ******/
declare #ActivityYear int = 2014
declare #ActivityYear1 int = 2015
declare #ActivityMonth int = 1
declare #ActivityMonth1 int = 3
Select FinancialCategory, ID, (CONVERT(varchar(5), ActivityMonth) + '-'
+ CONVERT(varchar(5), ActivityYear)) As [Month-Year], Sum(HoursCharged) As [Hours]
FROM Forecast
where (ActivityMonth between #ActivityMonth and #ActivityMonth1)
AND (ActivityYear between #ActivityYear and #ActivityYear1)
AND FinancialCategory = 'Forecast'
Group By FinancialCategory, ID,ActivityMonth, ActivityYear
This Outputs a table that looks like this:
And I would like to transpose it to have the hours for each ID broken out by the dates in the range. Note: this range of dates will be dynamic, I set initial dates for testing purposes.
I learnt a bit about dynamic pivot recently, this post helped a lot. As a practice I converted yours, which I think would look like this, but isn't tested as I haven't time tcreate tables etc at the moment. HTH.
declare #ActivityYear int = 2014
declare #ActivityYear1 int = 2015
declare #ActivityMonth int = 1
declare #ActivityMonth1 int = 3
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME((CONVERT(varchar(5), ActivityMonth) + '-'
+ CONVERT(varchar(5), ActivityYear)))
FROM Forecast
WHERE (ActivityMonth between #ActivityMonth and #ActivityMonth1)
AND (ActivityYear between #ActivityYear and #ActivityYear1)
AND FinancialCategory = 'Forecast'
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT FinancialCategory, ID, ' + #cols + ' FROM
(
SELECT FinancialCategory, ID, (CONVERT(varchar(5), ActivityMonth) + ''-''
+ CONVERT(varchar(5), ActivityYear)) As [Month-Year],HoursCharged
FROM Forecast
WHERE (ActivityMonth between ' + #ActivityMonth + ' and ' + #ActivityMonth1 + ')
AND (ActivityYear between ' + #ActivityYear + ' and ' +
#ActivityYear1 + ')
AND FinancialCategory = ''Forecast''
) x
PIVOT
(
Sum(HoursCharged)
for (CONVERT(varchar(5), ActivityMonth) + ''-''
+ CONVERT(varchar(5), ActivityYear)) in (' + #cols + ')
) p '
execute(#query)

Including default values on dynamic pivot

I am pivoting data in a table based on date, but need to return NULL for dates with no data.
Data in [dbo].[Metrics]:
The dynamic pivot SQL I am executing:
DECLARE #cols NVARCHAR(1000);
SELECT #cols =
STUFF((SELECT N'],[' + month_day
FROM (SELECT DISTINCT RIGHT(CONVERT(NCHAR(10), [Date], 126), 2)
FROM [Metrics]) AS O(month_day)
ORDER BY month_day
FOR XML PATH('')
), 1, 2, '') + N']';
DECLARE #sql NVARCHAR(2000);
SET #sql =
N'SELECT [Key], ' + #cols +
N' FROM (SELECT [Key], ' +
N' RIGHT(CONVERT(NCHAR(10), [Date], 126), 2) AS month_day, ' +
N' [Value] ' +
N' FROM [Metrics]) AS O ' +
N'PIVOT ' +
N'(SUM([Value]) FOR month_day IN (' + #cols + N')) AS P;';
EXECUTE(#sql);
...and the result set that dynamic SQL returns:
As you can see, since I do not have data for every day of the month it is simply not returning columns for those days. How can I have it return columns 10-23 and 28-31 with NULL for each cell? It should return columns 1-31 regardless of the actual month (e.g., 31 columns even when there are less than 31 days in a given month).
Any help is appreciated.
Then you don't really need that columns to be generated dynamically, since you always want them from 1 to 31. The easier way would be hardcoding those values on your pivot, really. Anyway, here is the way you can define your columns dynamically and still get all days:
SELECT #cols =
STUFF((SELECT N'],[' + CAST(number AS VARCHAR(2))
FROM (SELECT DISTINCT number
FROM master..[spt_values]
WHERE number BETWEEN 1 AND 31) AS O(number)
ORDER BY number
FOR XML PATH('')
), 1, 2, '') + N']';
And the version you really should be using is this:
SELECT *
FROM (SELECT [Key],
RIGHT(CONVERT(NCHAR(10), [Date], 126), 2) AS month_day,
[Value]
FROM [Metrics]) AS O
PIVOT (SUM([Value]) FOR month_day IN ([01],[02],[03],[04],[05],[06],[07],[08],[09],
[10],[11],[12],[13],[14],[15],[16],[17],[18],
[19],[20],[21],[22],[23],[24],[25],[26],[27],
[28],[29],[30],[31])) AS P;

Generating Scripts for Specific Records in SQL Server

This is probably a bit of a limited, but valuable scenario. I have a SQL Server 2008 database with a table that has millions of records. There appears to be an intermittent problem with several of the records. I'm trying to repro the problem. In an effort to do this, I finally got the ID of an offending record. I would like to generate an INSERT statement associated with this single record in my PROD database. Then I can easily migrate it into my TESTING database in an effort to repro and resolve the problem.
Basically, I need to generate a single INSERT statement for a single record from a single table where I know the primary key value of the record.
Does anyone have any ideas of how I can accomplish this? Essentially, I want to generate insert statements on a conditional basis.
Thank you!
First try to recreate what you want to insert with a SELECT statement.
After that you can insert into the table with a INSERT INTO like this:
INSERT INTO tablename
SELECT ....
If they are on different servers, you can use INSERT like this:
INSERT INTO tablename VALUES (...)
using the values given by the SELECT in the other server fill the values in the insert.
In your specific case I think you can do this:
CREATE PROCEDURE dbo.GenerateSingleInsert
#table NVARCHAR(511), -- expects schema.table notation
#pk_column SYSNAME, -- column that is primary key
#pk_value INT -- change data type accordingly
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cols NVARCHAR(MAX), #vals NVARCHAR(MAX),
#valOut NVARCHAR(MAX), #valSQL NVARCHAR(MAX);
SELECT #cols = N'', #vals = N'';
SELECT #cols = #cols + ',' + QUOTENAME(name),
#vals = #vals + ' + ' + REPLICATE(CHAR(39),3) + ','
+ REPLICATE(CHAR(39),3) + ' + ' + REPLICATE(CHAR(39),2) + '+'
+ 'RTRIM(' + CASE WHEN system_type_id IN (40,41,42,43,58,61) THEN
'CONVERT(CHAR(8), ' + QUOTENAME(name) + ', 112) + '' ''
+ CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
ELSE 'REPLACE(' + QUOTENAME(name) + ','''''''','''''''''''')' END + ')
+ ' + REPLICATE(CHAR(39),2)
FROM sys.columns WHERE [object_id] = OBJECT_ID(#table)
AND system_type_id <> 189 -- can't insert rowversion
AND is_computed = 0; -- can't insert computed columns
SELECT #cols = STUFF(#cols, 1, 1, ''),
#vals = REPLICATE(CHAR(39), 4) + ' + ' + STUFF(#vals, 1, 13, '')
+ REPLICATE(CHAR(39), 2);
SELECT #valSQL = N'SELECT #valOut = ' + #vals + ' FROM ' + #table + ' WHERE '
+ QUOTENAME(#pk_column) + ' = ''' + RTRIM(#pk_value) + ''';';
EXEC sp_executesql #valSQL, N'#valOut NVARCHAR(MAX) OUTPUT', #valOut OUTPUT;
SELECT SQL = 'INSERT ' + #table + '(' + #cols + ') SELECT ' + #valOut;
END
GO
So let's try it out:
CREATE TABLE dbo.splunge
(
ID INT, dt DATETIME, rv ROWVERSION, t NVARCHAR(MAX)
);
INSERT dbo.splunge(ID, dt, t)
SELECT 1, GETDATE(), 'foo'
UNION ALL SELECT 2, GETDATE(), 'bar'
UNION ALL SELECT 3, GETDATE(), 'O''Brien';
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 1;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '1','20120517 10:07:07:330','foo'
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 2;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '2','20120517 10:07:07:330','bar'
EXEC dbo.GenerateSingleInsert N'dbo.splunge', N'ID', 3;
SQL
-------------
INSERT dbo.splunge([ID],[dt],[t]) SELECT '3','20120517 10:07:07:330','O''Brien'
If there is an IDENTITY column you may need to set SET IDENTITY_INSERT ON for the TEST table, and verify that there is no collision. Probably about 500 caveats I should mention, I haven't tested all data types, etc.
However in the more general case there is a lot more to it than this. Vyas K has a pretty robust stored procedure that should demonstrate how complicated it can get:
http://vyaskn.tripod.com/code/generate_inserts_2005.txt
You are probably far better off using a tool like Red-Gate's SQL Data Compare to pick a specific row and generate an insert for you. As I've blogged about, paying for a tool is not just about the money, it's about the hours of troubleshooting and bug-fixing that someone else has already done for you.
Aaron,
I liked your code, it solved a problem for me. I ran into a few issues using it (like you said I would) with nulls and the text type so I made some changes to address those issues.
ALTER PROCEDURE dbo.GenerateSingleInsert
#table NVARCHAR(511), -- expects schema.table notation
#pk_column SYSNAME, -- column that is primary key
#pk_value INT -- change data type accordingly
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cols NVARCHAR(MAX), #vals NVARCHAR(MAX),
#valOut NVARCHAR(MAX), #valSQL NVARCHAR(MAX);
SELECT #cols = N'', #vals = N'';
SELECT #cols = #cols + ',' + QUOTENAME(name),
#vals = #vals + ' + '','' + ' + 'ISNULL('+REPLICATE(CHAR(39),4)+'+RTRIM(' +
CASE WHEN system_type_id IN (40,41,42,43,58,61) -- datetime types
THEN
'CONVERT(CHAR(8), ' + QUOTENAME(name) + ', 112) + '' ''+ CONVERT(CHAR(14), ' + QUOTENAME(name) + ', 14)'
WHEN system_type_id IN (35) -- text type NOTE: can overflow
THEN
'REPLACE(CAST(' + QUOTENAME(name) + 'as nvarchar(MAX)),'+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')'
ELSE
'REPLACE(' + QUOTENAME(name) + ','+REPLICATE(CHAR(39),4)+','+REPLICATE(CHAR(39),6)+')'
END
+ ')+' + REPLICATE(CHAR(39),4) + ',''null'') + '
FROM sys.columns WHERE [object_id] = OBJECT_ID(#table)
AND system_type_id <> 189 -- can't insert rowversion
AND is_computed = 0; -- can't insert computed columns
SELECT #cols = STUFF(#cols, 1, 1, ''),
#vals = REPLICATE(CHAR(39),2) + STUFF(#vals, 1, 6, '') + REPLICATE(CHAR(39),2) ;
SELECT #valSQL = N'SELECT #valOut = ' + #vals + ' FROM ' + #table + ' WHERE '
+ QUOTENAME(#pk_column) + ' = ''' + RTRIM(#pk_value) + ''';';
EXEC sp_executesql #valSQL, N'#valOut NVARCHAR(MAX) OUTPUT', #valOut OUTPUT;
SELECT SQL = 'INSERT ' + #table + '(' + #cols + ') SELECT ' + #valOut;
END