I would like to convert row data into columns, where the column names are not from the data. I think that using Pivot will not give me the correct solution. Please see Image of what my data looks like and how I want it to look.
The number of rows returned in the example will continue to grow over time.
My solution :
Based on #Triv answered, I have managed to solve the problem using the rank function to create a new column and then using dynamic pivot SQL to transform the data.
You could use a dynamic sql + PIVOT
CREATE TABLE #SampleData
(
AccountNumber int,
Product varchar(20),
ProductEndDate datetime
)
INSERT INTO #SampleData VALUES (1,'Fixed 10','2016-01-01'),(1,'Fixed 11','2016-02-01'),(1,'Fixed 13','2016-03-01'),(1,'Fixed 12','2016-04-01'),
(2,'Fixed 10','2016-01-01'),(2,'Fixed 11','2016-02-01'),(2,'Fixed 13','2016-03-01')
DECLARE #HeaderAll nvarchar(max)
DECLARE #ColumnPivotProduct nvarchar(max)
DECLARE #ColumnPivotProductEndDate nvarchar(max)
;WITH temp AS
(
SELECT DISTINCT
CONCAT('Product' ,row_number() OVER(PARTITION BY sd.AccountNumber ORDER BY sd.Product)) AS ProductGroup,
CONCAT('ProductEndDate' ,row_number() OVER(PARTITION BY sd.AccountNumber ORDER BY sd.Product)) AS ProductEndDateGroup
FROM #SampleData sd
)
SELECT #HeaderAll = STUFF((SELECT CONCAT(',',t.ProductGroup,'= MAX(',t.ProductGroup, '),', t.ProductEndDateGroup ,'= MAX(', t.ProductEndDateGroup,')') FROM temp t FOR XML PATH('')), 1,1,''),
#ColumnPivotProduct = STUFF((SELECT CONCAT(',',t.ProductGroup) FROM temp t FOR XML PATH('')), 1,1,''),
#ColumnPivotProductEndDate = STUFF((SELECT CONCAT(',', t.ProductEndDateGroup) FROM temp t FOR XML PATH('')), 1,1,'')
--SELECT #HeaderAll, #ColumnPivotProduct, #ColumnPivotProductEndDate
DECLARE #query nvarchar(max) = CONCAT(
';WITH temp AS
(
SELECT *,
CONCAT(''Product'' ,row_number() OVER(PARTITION BY sd.AccountNumber ORDER BY sd.Product)) AS ProductGroup,
CONCAT(''ProductEndDate'' ,row_number() OVER(PARTITION BY sd.AccountNumber ORDER BY sd.Product)) AS ProductEndDateGroup
FROM #SampleData sd
)
SELECT AccountNumber, ',#HeaderAll,' FROM
(
SELECT t.AccountNumber, t.Product, t.ProductEndDate, t.ProductGroup,t.ProductEndDateGroup FROM temp t
) src
PIVOT
(
MIN(Product) FOR ProductGroup IN (',#ColumnPivotProduct,')
) pvt
PIVOT
(
MIN(ProductEndDate) FOR ProductEndDateGroup IN (',#ColumnPivotProductEndDate,')
) pvt1
GROUP BY AccountNumber
')
PRINT #query
exec(#query)
DROP TABLE #SampleData
Demo link: http://rextester.com/AEQBZ56634
Note: CONCAT is avaiable in sql-server 2012+. IF you are using an older version, then use + for concatenating string
It works, check this out, Instead of #testTable use your table name
declare #temptable varchar(1000) = 'declare #tempTable1 table (',
#inserStatement varchar(1000) = 'insert into #tempTable1 (',
#insertValues varchar(1000) = ''
DECLARE #AcountNumber VARCHAR(50),#Product varchar(40),#ProductEndData varchar(50), #increment int = 0;
DECLARE db_cursor CURSOR FOR
select
Product,
ProductEndData from #testTable
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #Product, #ProductEndData
WHILE ##FETCH_STATUS = 0
BEGIN
set #increment = #increment+1;
SET #temptable += 'Product'+ cast(#increment as varchar) +' varchar(100),' + 'Product' + cast(#increment as varchar) + 'EndDate varchar(100),' ;
set #inserStatement += 'Product'+ cast(#increment as varchar) +',' + 'Product' + cast(#increment as varchar) + 'EndDate,';
set #insertValues += '(''' + #Product +''''+ ',' + ''''+ #ProductEndData + '''' + ')';
FETCH NEXT FROM db_cursor INTO #Product, #ProductEndData
END
CLOSE db_cursor
DEALLOCATE db_cursor
set #temptable = STUFF(#temptable, LEN(#temptable), 1, ')')
set #inserStatement = STUFF(#inserStatement, LEN(#inserStatement), 1, ')')
set #insertValues = replace(#insertValues, ')(', ',')
exec (#temptable + #inserStatement + ' values ' + #insertValues + 'select * from #tempTable1')
Related
I have a simple table. As a result I need to get names of numeric columns and paste like rows and then paste its values like columns.Here is an example of table:
As a result I need to receive something like this:
I tried to get the result with PIVOT, but I have not the correct answer.
select * from (
select col1, 'val' + cast(row_number()over(partition by col1 order by col1) as nvarchar(20)) ColVal
from mytbl
) tmp
pivot (
min(col1) for ColVal in (val1,val2)
) pvt
In this case you need to unpivot first, then pivot back:
DROP TABLE IF EXISTS dbo.temp
DROP TABLE IF EXISTS dbo.temp2
CREATE table dbo.temp(col1 INT, col2 INT, col3 INT);
INSERT INTO temp VALUES (27,93,80),(32,84,72),(46,68,75),(38,79,73),(23,77,84);
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
-- first unpivot to key value pairs
select #colsUnpivot
= stuff((select ','+quotename(C.column_name)
from information_schema.columns as C
where C.table_name = 'temp' and
C.column_name like 'col%'
for xml path('')), 1, 1, '')
set #query
= 'SELECT ''val'' + convert(varchar, i) as id,
name,
val
INTO dbo.temp2
FROM
(
SELECT *, row_number() over (order by col1) as i
from temp
) a
UNPIVOT
(
val
FOR name IN ('+ #colsunpivot +')
) u'
exec sp_executesql #query;
-- now pivot back
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', ' + QUOTENAME(id)
FROM (SELECT DISTINCT id FROM dbo.temp2) AS x;
SET #query = N'
SELECT name, ' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT id, name, val
from temp2
) AS j
PIVOT
(
SUM(val) FOR id IN ('
+ STUFF(REPLACE(#columns, ', [', ',['), 1, 1, '')
+ ')
) AS p;';
EXEC sp_executesql #query;
I adapted two separate scripts I had lying around, hence the two parts and the intermediate temp2 table. You can probably mash both together with a bit of elbow grease, but this should get you most of the way there.
Also adding the id (to get val1, val2 etc) dynamically means the results are sorted by col1 (val1 will have the lowest col1) but you were doing something similar in your attempt so I assume this is ok. If not, you will need to add an identity column to the data first and use that in place of the row_number()
How can I convert rows into columns and create different name for each column?
create table #TempTable (InvoiceNum int,State varchar(2), ChargeName varchar(50), PercentageRate decimal(5,3), FlatRate decimal(5,2))
insert into #TempTable values (235736, 'AZ','Inspection & Policy Fee', NULL,250.00)
,(235736, 'AZ','Surplus Line Tax',0.03,NULL)
,(235736, 'AZ','Stamping Fee',0.002,NULL
)
I need something like that:
UPDATE:
Using example I was able to unpivot it but the result is not what I wanted to:
create table #TempTable (InvoiceNum int,State varchar(2), ChargeName varchar(50), PercentageRate decimal(5,3), FlatRate decimal(5,2))
insert into #TempTable values (235736, 'AZ','Inspection & Policy Fee', NULL,250.00)
,(235736, 'AZ','Surplus Line Tax',0.03,NULL)
,(235736, 'AZ','Stamping Fee',0.002,NULL)
--select * from #TempTable
Declare #SQL nvarchar(max),
#query nvarchar(max)
select #SQL = STUFF((SELECT ',' + QUOTENAME(ChargeName)
from #TempTable
group by ChargeName, InvoiceNum
order by InvoiceNum
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--select #SQL
set #SQL = 'SELECT ' + #SQL + ' from
(
select PercentageRate, ChargeName
from #TempTable
) x
pivot
(
max(PercentageRate)
for ChargeName in (' + #SQL + ')
) p '
exec sp_executesql #SQL;
UPDATE:
Running below query gives me this:
Why ChargeName is not on the first row? I would expect to see it like this: What am I missing?
declare #TempTable table (InvoiceNum int,StateID varchar(2), ChargeName varchar(50), PercentageRate decimal(5,3), FlatRate decimal(5,2))
insert into #TempTable values (235736, 'AZ','Inspection & Policy Fee', NULL,250.00)
,(235736, 'AZ','Surplus Line Tax',0.03,NULL)
,(235736, 'AZ','Stamping Fee',0.002,NULL)
select
InvoiceNum,
ChargeName,
StateID,
PercentageRate,
FlatRate,
row_number() over (partition by InvoiceNum order by ChargeName) as RN
into #TempTable
from #TempTable #TempTable
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','')
+ QUOTENAME(RN)
FROM (SELECT DISTINCT RN FROM #TempTable) AS RN
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT InvoiceNum, ' + #ColumnName + '
FROM #TempTable
PIVOT(MAX(ChargeName)
FOR RN IN (' + #ColumnName + ')) AS PVTTable'
EXEC sp_executesql #DynamicPivotQuery
drop table #TempTable
I would just join the temp table multiple times as needed.
Given your #TempTable
SELECT T1.InvoiceNum,
Tax1_Jurisdiction = T1.State, Tax1_TaxType = T1.ChargeName, Tax1_Percent = T1.PercentageRate, Tax1_FixedRate = T1.FlatRate,
Tax2_Jurisdiction = T2.State, Tax2_TaxType = T2.ChargeName, Tax2_Percent = T2.PercentageRate, Tax2_FixedRate = T2.FlatRate,
Tax3_Jurisdiction = T3.State, Tax3_TaxType = T3.ChargeName, Tax3_Percent = T3.PercentageRate, Tax3_FixedRate = T3.FlatRate
FROM #TempTable T1
JOIN #TempTable T2 ON T1.InvoiceNum = T2.InvoiceNum
JOIN #TempTable T3 ON T1.InvoiceNum = T3.InvoiceNum
WHERE T1.ChargeName = 'Inspection & Policy Fee'
AND T2.ChargeName = 'Surplus Line Tax'
AND T3.ChargeName = 'Stamping Fee'
;
i have data in below format. this data is coming through SQL Query.
i want to show it in below format either by query or by rdlc report.
You need to use dynamic SQL to make it.
From your expected result you can try to follow thoes step to make it.
use row_number function make row number by Name, because we need to join base on that row_number.
get the use MAX and MIN to make row number calendar table. from 1 to max(rn). the table can let use outer join
declare a var #tables to make the OUTER JOIN execute SQL (each LEFT JOIN maen a group of Crew#).
declare a var #col to make column, which you want to select (Employee) from each table.
then use execute dynamic execute it.
look like this.
create table T
(
Name varchar(50),
Employee VARCHAR(50)
)
insert into T values ('Crew#1','TR123');
insert into T values ('Crew#1','311');
insert into T values ('Crew#2','DDD');
insert into T values ('Crew#2','12121');
insert into T values ('Crew#1','SDDAS');
insert into T values ('Crew#3','31114312');
insert into T values ('Crew#3','DD14124D');
insert into T values ('Crew#3','1214124121');
insert into T values ('Crew#3','SDD412AS');
DECLARE #tables AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#col AS NVARCHAR(MAX);
SET #tables = STUFF((SELECT distinct ' LEFT JOIN ' + ' (SELECT * FROM CTE WHERE Name = '''+Name+''') '+QUOTENAME(Name)+' on t1.smallRN = '+QUOTENAME(Name)+'.rn'
FROM T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #col = STUFF((SELECT distinct ', ' + QUOTENAME(Name)+'.Employee as '''+ QUOTENAME(Name) +''''
FROM T
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #col = substring(#col,1, len(#col))
set #query = '
WITH CTE AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) rn
FROM T
),CTE1 AS(
SELECT MIN(rn) smallRN,MAX(rn) bigRN
FROM CTE
UNION ALL
SELECT smallRN+1,bigRN
FROM CTE1
WHERE smallRN < bigRN
)
SELECT '+#col+'
FROM CTE1 t1 ' + #tables
execute(#query)
sqlfiddle
Creatin tbale
First we will create a temp table where we will stock the data that you have and your table
create table #table1
(
[Crew Name] varchar(500) ,
Employee varchar(500)
)
INsert into #table1
values (....)
select * from #table1
Dynamic selection
then we will create a dynamic query to get the columns that we have, that way we can add as much crews as we want,
declare #DynamicPivotQuery as nvarchar(max)
declare #ColumnName as nvarchar(max)
select #ColumnName = ISNULL(#ColumnName +',','') + QUOTENAME([Crew Name])
from (select distinct [Crew Name] from #table1) as Country
set #DynamicPivotQuery = N'select ' +#ColumnName + '
from #table1
Pivot ( MAX(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
'
exec (#DynamicPivotQuery)
this way we will get only the first row for every column
so we have to find a way to aggregate and get the other columns as well just to demonstrate i will union the Mmin also this is where i stoped my testes but you can do more then this with some testes
now the union :
declare #DynamicPivotQuery as nvarchar(max)
declare #ColumnName as nvarchar(max)
select #ColumnName = ISNULL(#ColumnName +',','') + QUOTENAME([Crew Name])
from (select distinct [Crew Name] from #table1) as Country
set #DynamicPivotQuery = N'select ' +#ColumnName + '
from #table1
Pivot ( MAX(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
union
select ' +#ColumnName + '
from #table1
Pivot ( MIN(Employee)
FOR [Crew Name] in (' +#ColumnName+')) as Pivoted
'
exec (#DynamicPivotQuery)
here is the result :
if you follow this way i'm sure that you will find a way to union all the result
You can add this result into a temp table
then add a column which will be a reference into this temp table
then use pivot function
To know more about pivot Visit :
https://msdn.microsoft.com/en-us/azure/data-lake-analytics/u-sql/pivot-and-unpivot-u-sql
you can use also SSIS to a very handy tool and easy to use
Using dynamic PIVOT if you dont have a set Crew columns.
DECLARE #ColumnString VARCHAR(256)
DECLARE #ColumnHeadrer VARCHAR(256)
DECLARE #sql varchar(1000)
CREATE TABLE #ColumnValue
(
Value VARCHAR(500),
ColumnHeader VARCHAR(256)
)
INSERT INTO #ColumnValue (Value, ColumnHeader)
SELECT DISTINCT '[' + CrewName + ']',
'ISNULL(' + CrewName + ','''') AS ' + CrewName
FROM CrewTable
SELECT #ColumnString = COALESCE(#ColumnString + ',', '') + Value,
#ColumnHeadrer = COALESCE(#ColumnHeadrer + ',', '') + ColumnHeader
FROM #ColumnValue
SET #sql =
'
SELECT ' + #ColumnHeadrer + '
FROM
(
SELECT Employee,
CrewName,
ROW_NUMBER() OVER(PARTITION BY CrewName ORDER BY CrewName) AS rnk
FROM CrewTable
) AS P
PIVOT
(
MAX(Employee) FOR [CrewName] IN ('+#ColumnString+')
) AS pv
'
EXEC (#sql)
I need to select all unique values from all columns in a table.
I have tried to implement the query below which I found in the thread How to get unique values from all columns of a table in SQL Server.
declare #Sql_Str varchar(8000)='';
select #Sql_Str=#Sql_Str+' select cast (' +name +' as varchar(500))
from <yourtable> union'
from sys.columns
where [object_id]=object_id('<yourtable>');
set #Sql_Str=SUBSTRING(#Sql_Str,1,len(#Sql_Str)-6);
exec(#Sql_Str)
I cannot get that query to work however. My table has 118 columns. I think that may be more data than the query above may handle.
Try something like this:
DECLARE #Schema VARCHAR(500)='dbo';
DECLARE #tableName VARCHAR(500)='SomeTable';
DECLARE #cmd NVARCHAR(MAX)=
(
SELECT STUFF(
(
SELECT ' UNION ALL SELECT ''' + c.TABLE_SCHEMA + ''' AS TableSchema '
+ ',''' + c.TABLE_NAME + ''' AS TableName '
+ ',''' + c.COLUMN_NAME + ''' AS ColumnName '
+ ',''' + c.DATA_TYPE + ''' AS ColumnType '
+ ',CAST(' + QUOTENAME(c.COLUMN_NAME)+' AS NVARCHAR(MAX)) AS Value '
+ ' FROM ' + QUOTENAME(c.TABLE_SCHEMA) + '.' + QUOTENAME(c.TABLE_NAME)
+ ' WHERE ' + QUOTENAME(c.COLUMN_NAME) + ' IS NOT NULL '
+ ' GROUP BY ' + QUOTENAME(c.COLUMN_NAME) + ' '
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE TABLE_NAME=#TableName
AND TABLE_SCHEMA=#Schema
--exclude not supported types
--AND c.DATA_TYPE NOT IN('xml') --add more types
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)'),1,10,'')
);
--PRINT #cmd
EXEC(#cmd);
This statement will first create a long list of UNION ALL SELECT with GROUP BY (better than DISTINCT) as dynamically created SQL and executes this with EXEC().
You can decomment PRINT to examine the statement created.
This should work in tSQL:
declare #table_name varchar(55)
set #table_name= 'IV00101' ---- <-- Change this to your table name
create table #colcount (
colname varchar(55),
dct int,
tot int
)
create table #colContent (
colname varchar(55),
col_val nvarchar(max),
col_val_count int
)
create table #sqlexecs( s varchar(max))
declare #col_name varchar(max), #sql nvarchar(max), #sql2 nvarchar(max)
declare c cursor for
select name from sys.columns where [object_id]=object_id(#table_name)
open c
fetch next from c into #col_name
while ##FETCH_STATUS = 0
begin
set #sql = 'select cn.name, count(distinct '+#col_name+') as dct_numrow, count('+#col_name+') as tot_numrow from '+#table_name+' join (select name from sys.columns where name = '''+#col_name+''' and [object_id]=object_id('''+#table_name+''')) cn on cn.name = '''+#col_name+''' group by cn.name'
set #sql2 = 'select ' +#col_name+', count('+#col_name+') as colvalcnt from '+#table_name+' group by '+#col_name
--insert into #sqlexecs values (#sql) --uncomment to view sql selects produced by #sql
--insert into #sqlexecs values (#sql2) --uncomment to view sql selects produced by #sql2
insert into #colcount execute sp_executesql #sql
------
declare #d int, #t int
set #d = (select dct from #colcount where colname = #col_name)
set #t = (select tot from #colcount where colname = #col_name)
if (#d <> #t)
begin
insert into #colContent (colname) values (#col_name)
insert into #colContent (col_val,col_val_count) execute sp_executesql #sql2
end
else
begin
insert into #colContent values (#col_name,1,1)
end
fetch next from c into #col_name
end
close c
deallocate c
--select * from #sqlexecs -- uncomment to view sql code produced by #sql and #sql2
select * from #colcount --order by dct desc
select * from #colContent
drop table #colcount
drop table #colContent
drop table #sqlexecs
The first table shows column name, distinct value count, and total value count.
The second table shows column name, distinct values, and the number of times a distinct value appears. If values in column are all distinct (column is a candidate key), colname | 1 | 1 is shown. This should work if copy/pasted, please let me know it doesn't. Dev for use in Dynamics GP.
I am working on building a procedure that uses basic dynamic SQL. I want to use the result of the dynamic SQL (#query) in another part of said procedure. Below is a shorthand version of the code I am attempting to complete.
WITHOUT THE USE OF sp_executesql, how can I go about passing the result value of #query into the IF blocks?
DECLARE #table VARCHAR(MAX)
DECLARE #query VARCHAR(MAX)
DECLARE #map VARCHAR(MAX)
SET #table = 'SomeTable'
SET #query = '
;WITH Assignment AS
(
SELECT
''' + #table + ''' AS src
,Type
,RANK () OVER(ORDER BY COUNT(type) as rnk
FROM ' + #table + '
GROUP BY Type
)
SELECT Type
FROM Assignment
WHERE rnk = ''1'''
IF (#query = 'typeA')
BEGIN
/* preform an upsert dynamically */
END
IF (#query = 'typeB')
BEGIN
/* preform a delete dynamically */
END
IF (#query = 'typeC')
BEGIN
/* preform an alter dynamically */
END
Why are you testing #query right after it has been set with some SQL?
You could do it with a temp table:
Create Table #temp(type...)
SET #query = '
;WITH Assignment AS
(
SELECT
''' + #table + ''' AS src
,Type
,RANK () OVER(ORDER BY COUNT(type) as rnk
FROM ' + #table + '
GROUP BY Type
)
Insert Into #temp(type)
SELECT Type
FROM Assignment
WHERE rnk = ''1'''
You can also build your dynamic query in your if statement although I am not sure it would work in your case:
SET #q1 = '
;WITH Assignment AS
(
SELECT
''' + #table + ''' AS src
,Type
,RANK () OVER(ORDER BY COUNT(type) as rnk
FROM ' + #table + '
GROUP BY Type
)'
set #q2 = 'SELECT Type
FROM Assignment
WHERE rnk = ''1'''
Case When #type = 'A' then #query = #q1 + 'Insert into... ' + #q2
Case When #type = 'B' then #query = #q1 + 'Update... ' + #q2
Case When #type = 'B' then #query = #q1 + 'delete from where type in (' + #q2 + ')' end
If you change you mind, it is also easy with sp_executesql:
create table #temp(type int)
insert into #temp
exec sp_executesql #query
or if there are not thousands of rows:
declare #temp table(type int)
insert into #temp
exec sp_executesql #query
If there is only one row, still with sp_executesql and a parameter, this is the best option:
declare #type varchar(10)
SET #query = '
declare #type varchar(10)
;WITH Assignment AS
(
SELECT
''' + #table + ''' AS src
,Type
,RANK () OVER(ORDER BY COUNT(type) as rnk
FROM ' + #table + '
GROUP BY Type
)
SELECT #type = Type
FROM Assignment
WHERE rnk = ''1''';
exec sp_executesql #query, N'#type varchar(10)', #type = #type
This is one way to get data out of dynamic SQL
DECLARE #SQL VARCHAR(MAX)
--Dynamic SQL
SET #SQL = '
--Do anything you like in here as long as you select the results in the #Data Table format at the end
SELECT 132'
--How to get the result out of the dynamic SQL (into a table)
DECLARE #Data TABLE (Value INT)
INSERT INTO #Data(Value)
EXEC(#SQL)
--Get the result out of the table into a local (if you need to)
DECLARE #MyValue INT
SELECT #MyValue = Value FROM #Data
--Do what you like with the value now we are back in normal SQL
PRINT #MyValue