Related
I have read the stuff on MS pivot tables and I am still having problems getting this correct.
I have a temp table that is being created, we will say that column 1 is a Store number, and column 2 is a week number and lastly column 3 is a total of some type. Also the Week numbers are dynamic, the store numbers are static.
Store Week xCount
------- ---- ------
102 1 96
101 1 138
105 1 37
109 1 59
101 2 282
102 2 212
105 2 78
109 2 97
105 3 60
102 3 123
101 3 220
109 3 87
I would like it to come out as a pivot table, like this:
Store 1 2 3 4 5 6....
-----
101 138 282 220
102 96 212 123
105 37
109
Store numbers down the side and weeks across the top.
If you are using SQL Server 2005+, then you can use the PIVOT function to transform the data from rows into columns.
It sounds like you will need to use dynamic sql if the weeks are unknown but it is easier to see the correct code using a hard-coded version initially.
First up, here are some quick table definitions and data for use:
CREATE TABLE yt
(
[Store] int,
[Week] int,
[xCount] int
);
INSERT INTO yt
(
[Store],
[Week], [xCount]
)
VALUES
(102, 1, 96),
(101, 1, 138),
(105, 1, 37),
(109, 1, 59),
(101, 2, 282),
(102, 2, 212),
(105, 2, 78),
(109, 2, 97),
(105, 3, 60),
(102, 3, 123),
(101, 3, 220),
(109, 3, 87);
If your values are known, then you will hard-code the query:
select *
from
(
select store, week, xCount
from yt
) src
pivot
(
sum(xcount)
for week in ([1], [2], [3])
) piv;
See SQL Demo
Then if you need to generate the week number dynamically, your code will be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Week)
from yt
group by Week
order by Week
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT store,' + #cols + ' from
(
select store, week, xCount
from yt
) x
pivot
(
sum(xCount)
for week in (' + #cols + ')
) p '
execute(#query);
See SQL Demo.
The dynamic version, generates the list of week numbers that should be converted to columns. Both give the same result:
| STORE | 1 | 2 | 3 |
---------------------------
| 101 | 138 | 282 | 220 |
| 102 | 96 | 212 | 123 |
| 105 | 37 | 78 | 60 |
| 109 | 59 | 97 | 87 |
This is for dynamic # of weeks.
Full example here:SQL Dynamic Pivot
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','') + QUOTENAME(Week)
FROM (SELECT DISTINCT Week FROM #StoreSales) AS Weeks
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT Store, ' + #ColumnName + '
FROM #StoreSales
PIVOT(SUM(xCount)
FOR Week IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
I've achieved the same thing before by using subqueries. So if your original table was called StoreCountsByWeek, and you had a separate table that listed the Store IDs, then it would look like this:
SELECT StoreID,
Week1=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=1),
Week2=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=2),
Week3=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=3)
FROM Store
ORDER BY StoreID
One advantage to this method is that the syntax is more clear and it makes it easier to join to other tables to pull other fields into the results too.
My anecdotal results are that running this query over a couple of thousand rows completed in less than one second, and I actually had 7 subqueries. But as noted in the comments, it is more computationally expensive to do it this way, so be careful about using this method if you expect it to run on large amounts of data .
This is what you can do:
SELECT *
FROM yourTable
PIVOT (MAX(xCount)
FOR Week in ([1],[2],[3],[4],[5],[6],[7])) AS pvt
DEMO
I'm writing an sp that could be useful for this purpose, basically this sp pivot any table and return a new table pivoted or return just the set of data, this is the way to execute it:
Exec dbo.rs_pivot_table #schema=dbo,#table=table_name,#column=column_to_pivot,#agg='sum([column_to_agg]),avg([another_column_to_agg]),',
#sel_cols='column_to_select1,column_to_select2,column_to_select1',#new_table=returned_table_pivoted;
please note that in the parameter #agg the column names must be with '[' and the parameter must end with a comma ','
SP
Create Procedure [dbo].[rs_pivot_table]
#schema sysname=dbo,
#table sysname,
#column sysname,
#agg nvarchar(max),
#sel_cols varchar(max),
#new_table sysname,
#add_to_col_name sysname=null
As
--Exec dbo.rs_pivot_table dbo,##TEMPORAL1,tip_liq,'sum([val_liq]),sum([can_liq]),','cod_emp,cod_con,tip_liq',##TEMPORAL1PVT,'hola';
Begin
Declare #query varchar(max)='';
Declare #aggDet varchar(100);
Declare #opp_agg varchar(5);
Declare #col_agg varchar(100);
Declare #pivot_col sysname;
Declare #query_col_pvt varchar(max)='';
Declare #full_query_pivot varchar(max)='';
Declare #ind_tmpTbl int; --Indicador de tabla temporal 1=tabla temporal global 0=Tabla fisica
Create Table #pvt_column(
pivot_col varchar(100)
);
Declare #column_agg table(
opp_agg varchar(5),
col_agg varchar(100)
);
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#table) AND type in (N'U'))
Set #ind_tmpTbl=0;
ELSE IF OBJECT_ID('tempdb..'+ltrim(rtrim(#table))) IS NOT NULL
Set #ind_tmpTbl=1;
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#new_table) AND type in (N'U')) OR
OBJECT_ID('tempdb..'+ltrim(rtrim(#new_table))) IS NOT NULL
Begin
Set #query='DROP TABLE '+#new_table+'';
Exec (#query);
End;
Select #query='Select distinct '+#column+' From '+(case when #ind_tmpTbl=1 then 'tempdb.' else '' end)+#schema+'.'+#table+' where '+#column+' is not null;';
Print #query;
Insert into #pvt_column(pivot_col)
Exec (#query)
While charindex(',',#agg,1)>0
Begin
Select #aggDet=Substring(#agg,1,charindex(',',#agg,1)-1);
Insert Into #column_agg(opp_agg,col_agg)
Values(substring(#aggDet,1,charindex('(',#aggDet,1)-1),ltrim(rtrim(replace(substring(#aggDet,charindex('[',#aggDet,1),charindex(']',#aggDet,1)-4),')',''))));
Set #agg=Substring(#agg,charindex(',',#agg,1)+1,len(#agg))
End
Declare cur_agg cursor read_only forward_only local static for
Select
opp_agg,col_agg
from #column_agg;
Open cur_agg;
Fetch Next From cur_agg
Into #opp_agg,#col_agg;
While ##fetch_status=0
Begin
Declare cur_col cursor read_only forward_only local static for
Select
pivot_col
From #pvt_column;
Open cur_col;
Fetch Next From cur_col
Into #pivot_col;
While ##fetch_status=0
Begin
Select #query_col_pvt='isnull('+#opp_agg+'(case when '+#column+'='+quotename(#pivot_col,char(39))+' then '+#col_agg+
' else null end),0) as ['+lower(Replace(Replace(#opp_agg+'_'+convert(varchar(100),#pivot_col)+'_'+replace(replace(#col_agg,'[',''),']',''),' ',''),'&',''))+
(case when #add_to_col_name is null then space(0) else '_'+isnull(ltrim(rtrim(#add_to_col_name)),'') end)+']'
print #query_col_pvt
Select #full_query_pivot=#full_query_pivot+#query_col_pvt+', '
--print #full_query_pivot
Fetch Next From cur_col
Into #pivot_col;
End
Close cur_col;
Deallocate cur_col;
Fetch Next From cur_agg
Into #opp_agg,#col_agg;
End
Close cur_agg;
Deallocate cur_agg;
Select #full_query_pivot=substring(#full_query_pivot,1,len(#full_query_pivot)-1);
Select #query='Select '+#sel_cols+','+#full_query_pivot+' into '+#new_table+' From '+(case when #ind_tmpTbl=1 then 'tempdb.' else '' end)+
#schema+'.'+#table+' Group by '+#sel_cols+';';
print #query;
Exec (#query);
End;
GO
This is an example of execution:
Exec dbo.rs_pivot_table #schema=dbo,#table=##TEMPORAL1,#column=tip_liq,#agg='sum([val_liq]),avg([can_liq]),',#sel_cols='cod_emp,cod_con,tip_liq',#new_table=##TEMPORAL1PVT;
then Select * From ##TEMPORAL1PVT would return:
Here is a revision of #Tayrn answer above that might help you understand pivoting a little easier:
This may not be the best way to do this, but this is what helped me wrap my head around how to pivot tables.
ID = rows you want to pivot
MY_KEY = the column you are selecting from your original table that contains the column names you want to pivot.
VAL = the value you want returning under each column.
MAX(VAL) => Can be replaced with other aggregiate functions. SUM(VAL), MIN(VAL), ETC...
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(MY_KEY)
from yt
group by MY_KEY
order by MY_KEY ASC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ID,' + #cols + ' from
(
select ID, MY_KEY, VAL
from yt
) x
pivot
(
sum(VAL)
for MY_KEY in (' + #cols + ')
) p '
execute(#query);
select * from (select name, ID from Empoyee) Visits
pivot(sum(ID) for name
in ([Emp1],
[Emp2],
[Emp3]
) ) as pivottable;
Just give you some idea how other databases solve this problem. DolphinDB also has built-in support for pivoting and the sql looks much more intuitive and neat. It is as simple as specifying the key column (Store), pivoting column (Week), and the calculated metric (sum(xCount)).
//prepare a 10-million-row table
n=10000000
t=table(rand(100, n) + 1 as Store, rand(54, n) + 1 as Week, rand(100, n) + 1 as xCount)
//use pivot clause to generate a pivoted table pivot_t
pivot_t = select sum(xCount) from t pivot by Store, Week
DolphinDB is a columnar high performance database. The calculation in the demo costs as low as 546 ms on a dell xps laptop (i7 cpu). To get more details, please refer to online DolphinDB manual https://www.dolphindb.com/help/index.html?pivotby.html
Pivot is one of the SQL operator which is used to turn the unique data from one column into multiple column in the output. This is also mean by transforming the rows into columns (rotating table). Let us consider this table,
If I want to filter this data based on the types of product (Speaker, Glass, Headset) by each customer, then use Pivot operator.
Select CustmerName, Speaker, Glass, Headset
from TblCustomer
Pivot
(
Sum(Price) for Product in ([Speaker],[Glass],[Headset])
) as PivotTable
I have read the stuff on MS pivot tables and I am still having problems getting this correct.
I have a temp table that is being created, we will say that column 1 is a Store number, and column 2 is a week number and lastly column 3 is a total of some type. Also the Week numbers are dynamic, the store numbers are static.
Store Week xCount
------- ---- ------
102 1 96
101 1 138
105 1 37
109 1 59
101 2 282
102 2 212
105 2 78
109 2 97
105 3 60
102 3 123
101 3 220
109 3 87
I would like it to come out as a pivot table, like this:
Store 1 2 3 4 5 6....
-----
101 138 282 220
102 96 212 123
105 37
109
Store numbers down the side and weeks across the top.
If you are using SQL Server 2005+, then you can use the PIVOT function to transform the data from rows into columns.
It sounds like you will need to use dynamic sql if the weeks are unknown but it is easier to see the correct code using a hard-coded version initially.
First up, here are some quick table definitions and data for use:
CREATE TABLE yt
(
[Store] int,
[Week] int,
[xCount] int
);
INSERT INTO yt
(
[Store],
[Week], [xCount]
)
VALUES
(102, 1, 96),
(101, 1, 138),
(105, 1, 37),
(109, 1, 59),
(101, 2, 282),
(102, 2, 212),
(105, 2, 78),
(109, 2, 97),
(105, 3, 60),
(102, 3, 123),
(101, 3, 220),
(109, 3, 87);
If your values are known, then you will hard-code the query:
select *
from
(
select store, week, xCount
from yt
) src
pivot
(
sum(xcount)
for week in ([1], [2], [3])
) piv;
See SQL Demo
Then if you need to generate the week number dynamically, your code will be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Week)
from yt
group by Week
order by Week
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT store,' + #cols + ' from
(
select store, week, xCount
from yt
) x
pivot
(
sum(xCount)
for week in (' + #cols + ')
) p '
execute(#query);
See SQL Demo.
The dynamic version, generates the list of week numbers that should be converted to columns. Both give the same result:
| STORE | 1 | 2 | 3 |
---------------------------
| 101 | 138 | 282 | 220 |
| 102 | 96 | 212 | 123 |
| 105 | 37 | 78 | 60 |
| 109 | 59 | 97 | 87 |
This is for dynamic # of weeks.
Full example here:SQL Dynamic Pivot
DECLARE #DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
--Get distinct values of the PIVOT Column
SELECT #ColumnName= ISNULL(#ColumnName + ',','') + QUOTENAME(Week)
FROM (SELECT DISTINCT Week FROM #StoreSales) AS Weeks
--Prepare the PIVOT query using the dynamic
SET #DynamicPivotQuery =
N'SELECT Store, ' + #ColumnName + '
FROM #StoreSales
PIVOT(SUM(xCount)
FOR Week IN (' + #ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql #DynamicPivotQuery
I've achieved the same thing before by using subqueries. So if your original table was called StoreCountsByWeek, and you had a separate table that listed the Store IDs, then it would look like this:
SELECT StoreID,
Week1=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=1),
Week2=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=2),
Week3=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=3)
FROM Store
ORDER BY StoreID
One advantage to this method is that the syntax is more clear and it makes it easier to join to other tables to pull other fields into the results too.
My anecdotal results are that running this query over a couple of thousand rows completed in less than one second, and I actually had 7 subqueries. But as noted in the comments, it is more computationally expensive to do it this way, so be careful about using this method if you expect it to run on large amounts of data .
This is what you can do:
SELECT *
FROM yourTable
PIVOT (MAX(xCount)
FOR Week in ([1],[2],[3],[4],[5],[6],[7])) AS pvt
DEMO
I'm writing an sp that could be useful for this purpose, basically this sp pivot any table and return a new table pivoted or return just the set of data, this is the way to execute it:
Exec dbo.rs_pivot_table #schema=dbo,#table=table_name,#column=column_to_pivot,#agg='sum([column_to_agg]),avg([another_column_to_agg]),',
#sel_cols='column_to_select1,column_to_select2,column_to_select1',#new_table=returned_table_pivoted;
please note that in the parameter #agg the column names must be with '[' and the parameter must end with a comma ','
SP
Create Procedure [dbo].[rs_pivot_table]
#schema sysname=dbo,
#table sysname,
#column sysname,
#agg nvarchar(max),
#sel_cols varchar(max),
#new_table sysname,
#add_to_col_name sysname=null
As
--Exec dbo.rs_pivot_table dbo,##TEMPORAL1,tip_liq,'sum([val_liq]),sum([can_liq]),','cod_emp,cod_con,tip_liq',##TEMPORAL1PVT,'hola';
Begin
Declare #query varchar(max)='';
Declare #aggDet varchar(100);
Declare #opp_agg varchar(5);
Declare #col_agg varchar(100);
Declare #pivot_col sysname;
Declare #query_col_pvt varchar(max)='';
Declare #full_query_pivot varchar(max)='';
Declare #ind_tmpTbl int; --Indicador de tabla temporal 1=tabla temporal global 0=Tabla fisica
Create Table #pvt_column(
pivot_col varchar(100)
);
Declare #column_agg table(
opp_agg varchar(5),
col_agg varchar(100)
);
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#table) AND type in (N'U'))
Set #ind_tmpTbl=0;
ELSE IF OBJECT_ID('tempdb..'+ltrim(rtrim(#table))) IS NOT NULL
Set #ind_tmpTbl=1;
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#new_table) AND type in (N'U')) OR
OBJECT_ID('tempdb..'+ltrim(rtrim(#new_table))) IS NOT NULL
Begin
Set #query='DROP TABLE '+#new_table+'';
Exec (#query);
End;
Select #query='Select distinct '+#column+' From '+(case when #ind_tmpTbl=1 then 'tempdb.' else '' end)+#schema+'.'+#table+' where '+#column+' is not null;';
Print #query;
Insert into #pvt_column(pivot_col)
Exec (#query)
While charindex(',',#agg,1)>0
Begin
Select #aggDet=Substring(#agg,1,charindex(',',#agg,1)-1);
Insert Into #column_agg(opp_agg,col_agg)
Values(substring(#aggDet,1,charindex('(',#aggDet,1)-1),ltrim(rtrim(replace(substring(#aggDet,charindex('[',#aggDet,1),charindex(']',#aggDet,1)-4),')',''))));
Set #agg=Substring(#agg,charindex(',',#agg,1)+1,len(#agg))
End
Declare cur_agg cursor read_only forward_only local static for
Select
opp_agg,col_agg
from #column_agg;
Open cur_agg;
Fetch Next From cur_agg
Into #opp_agg,#col_agg;
While ##fetch_status=0
Begin
Declare cur_col cursor read_only forward_only local static for
Select
pivot_col
From #pvt_column;
Open cur_col;
Fetch Next From cur_col
Into #pivot_col;
While ##fetch_status=0
Begin
Select #query_col_pvt='isnull('+#opp_agg+'(case when '+#column+'='+quotename(#pivot_col,char(39))+' then '+#col_agg+
' else null end),0) as ['+lower(Replace(Replace(#opp_agg+'_'+convert(varchar(100),#pivot_col)+'_'+replace(replace(#col_agg,'[',''),']',''),' ',''),'&',''))+
(case when #add_to_col_name is null then space(0) else '_'+isnull(ltrim(rtrim(#add_to_col_name)),'') end)+']'
print #query_col_pvt
Select #full_query_pivot=#full_query_pivot+#query_col_pvt+', '
--print #full_query_pivot
Fetch Next From cur_col
Into #pivot_col;
End
Close cur_col;
Deallocate cur_col;
Fetch Next From cur_agg
Into #opp_agg,#col_agg;
End
Close cur_agg;
Deallocate cur_agg;
Select #full_query_pivot=substring(#full_query_pivot,1,len(#full_query_pivot)-1);
Select #query='Select '+#sel_cols+','+#full_query_pivot+' into '+#new_table+' From '+(case when #ind_tmpTbl=1 then 'tempdb.' else '' end)+
#schema+'.'+#table+' Group by '+#sel_cols+';';
print #query;
Exec (#query);
End;
GO
This is an example of execution:
Exec dbo.rs_pivot_table #schema=dbo,#table=##TEMPORAL1,#column=tip_liq,#agg='sum([val_liq]),avg([can_liq]),',#sel_cols='cod_emp,cod_con,tip_liq',#new_table=##TEMPORAL1PVT;
then Select * From ##TEMPORAL1PVT would return:
Here is a revision of #Tayrn answer above that might help you understand pivoting a little easier:
This may not be the best way to do this, but this is what helped me wrap my head around how to pivot tables.
ID = rows you want to pivot
MY_KEY = the column you are selecting from your original table that contains the column names you want to pivot.
VAL = the value you want returning under each column.
MAX(VAL) => Can be replaced with other aggregiate functions. SUM(VAL), MIN(VAL), ETC...
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(MY_KEY)
from yt
group by MY_KEY
order by MY_KEY ASC
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ID,' + #cols + ' from
(
select ID, MY_KEY, VAL
from yt
) x
pivot
(
sum(VAL)
for MY_KEY in (' + #cols + ')
) p '
execute(#query);
select * from (select name, ID from Empoyee) Visits
pivot(sum(ID) for name
in ([Emp1],
[Emp2],
[Emp3]
) ) as pivottable;
Just give you some idea how other databases solve this problem. DolphinDB also has built-in support for pivoting and the sql looks much more intuitive and neat. It is as simple as specifying the key column (Store), pivoting column (Week), and the calculated metric (sum(xCount)).
//prepare a 10-million-row table
n=10000000
t=table(rand(100, n) + 1 as Store, rand(54, n) + 1 as Week, rand(100, n) + 1 as xCount)
//use pivot clause to generate a pivoted table pivot_t
pivot_t = select sum(xCount) from t pivot by Store, Week
DolphinDB is a columnar high performance database. The calculation in the demo costs as low as 546 ms on a dell xps laptop (i7 cpu). To get more details, please refer to online DolphinDB manual https://www.dolphindb.com/help/index.html?pivotby.html
Pivot is one of the SQL operator which is used to turn the unique data from one column into multiple column in the output. This is also mean by transforming the rows into columns (rotating table). Let us consider this table,
If I want to filter this data based on the types of product (Speaker, Glass, Headset) by each customer, then use Pivot operator.
Select CustmerName, Speaker, Glass, Headset
from TblCustomer
Pivot
(
Sum(Price) for Product in ([Speaker],[Glass],[Headset])
) as PivotTable
I have the below data format in my tables, a customer answers test questions and those responses are saved in a table. Questions are grouped as sections. I am trying to get responses associated to a test and for certain sections.
Sectiontable
SecID SecName
1 Box
2 square
3 circle
CustomerTable
CID CName
92 John
93 Andrew
94 Chris
TestTable
testID testkey SecID
18 T1 1
19 T11 1
21 T2 2
22 T21 2
34 T3 3
35 T4 3
CustomerTestresponse
responseID CID testID responseText
1 92 18 T1Text
2 92 19 T11Text
3 92 34 T3Text
4 92 35 T4Text
5 92 22 T21Text
6 93 19 Myresponse
7 93 34 vendor
8 93 21 cutomerout
Expected Query output:
CID T1KeyResponse T11KeyResponse T3KeyResponse T4KeyResponse
92 T1Text T11Text T3Text T4Text
93 Myresponse vendor
It is a FOUR (4) step solution
-- adapted with great reverence from https://dba.stackexchange.com/questions/119844/how-to-pivot-without-fixed-columns-in-tsql
--(1) Declare variables
DECLARE
#cols AS NVARCHAR(MAX)
,#query AS NVARCHAR(MAX)
;
--(2) Concatenate list of new column headers, using FOR XML PATH and STUFF:
-- see Where SecID <> 2 (you may want to change this)
SELECT #cols =
STUFF(
(SELECT DISTINCT
',' + QUOTENAME(t.testkey + 'KeyResponse')
FROM qandrResponse as r
Left Join qandrTest as t
On r.testID = t.testID
Where SecID <> 2 -- you may want to filter differently
Order By ',' + QUOTENAME(t.testkey + 'KeyResponse')
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,''
);
-- Select #cols; -- uncomment to see the list, drag right side open to see full list
--(3) Create the full sql string that will be executed, using #cols from above
set #query
= N'SELECT CID, ' + #cols + N'
From (SELECT
r.CID
,t.testkey + ''' + 'KeyResponse' + N''' as Keys
,responseText
FROM qandrResponse as r
Left Join qandrTest as t
On r.testID = t.testID
) x
Pivot (
max(responseText) for Keys IN (' + #cols + N')
) as p';
-- Select #query; -- uncomment to see sql, prettyprint with http://www.dpriver.com/pp/sqlformat.htm
--(4) Execute the query
exec sp_executesql #query
-- Results
--CID T11KeyResponse T1KeyResponse T3KeyResponse T4KeyResponse
--92 T11Text T1Text T3Text T4Text
--93 Myresponse NULL vendor NULL
I have a requirement as mentioned below. Query needs to fetch data on SQL Server 2008 R2 database.
Tried my best to fetch the data as mentioned below but couldn't. Really appreciate your help.
Product_table
SKU UPC Details Weight Color
223 111 TShirt 25 White
224 114 Pants 25 Black
225 115 Abc 29 Yellow
230 116 XyX 23 Pink
226 117 AXYz 25 Red
226 118 Abdc 26 White
228 119 Abcr 20 Pink
229 120 Abcy 22 Green
Custom_tbl
SKU Custom_name Custom_value
223 Pickup true
223 eligible false
223 size medium
223 map red
224 pickup false
224 eligible false
224 map green
225 Pickup true
225 eligible true
225 size large
225 department 001
225 availability true
226 Pickup true
226 size large
226 map blue
226 availability true
229 eligible true
From the above two tables mentioned query needs to fetch data as mentioned below.
Note: CustomValues (sixth column in example table) are comma separated and I need only 3 values in order (Pickup, eligible, map columns in Custom_tbl and rest needs to be ignored. If any custom_name is not available then it should return empty string (Check from 3rd row) as shown in below)
SKU UPC DETAILS WEIGHT COLOR CustomValues
223 111 TShirt 25 White true,false,red
224 114 Pants 25 Black false,false,green
225 115 Abc 29 Yellow true,true,
226 118 Abdc 26 White true,,blue
228 119 Abcr 20 Pink ,,,
229 120 Abcy 22 Green ,true,
Can any help me in modifying the query for same above data but I need to exclude data whose Custom_name is eligible and value is false. I have this query which gets what I am looking for the original question but unable to add the condition what I am looking for. Appreciate your help on this.
For same above data I need to exclude data whose Custom_name eligible and value is false. I have this query but unable to add above logic
SELECT productDetails.sku, Isnull(productDetails.sku, '') + ','
+ Isnull(productDetails.upc, '') + ','
+ Isnull(productDetails.details, '')
+ ',' + CustomValues
FROM (SELECT PD.*,
Stuff((SELECT ',' + Attributes.customval
FROM (SELECT
A.sku,
Isnull(Max(A.[Pickup]), '') + ','
+ Isnull(Max(A.[eligible]), '') + ','
+ Isnull(Max(A.[size]), '') AS customVal
FROM ( SELECT sku, Isnull(CASE WHEN custom_name = 'Pickup' THEN Max(custom_value) END, '') AS 'Pickup',
Isnull(CASE WHEN custom_name = 'eligible' THEN Max(custom_value) END, '') AS 'eligible',
Isnull(CASE WHEN custom_name = 'size' THEN Max(custom_value) END, '') AS 'size'
FROM [product_custom_details]
GROUP BY sku, custom_field_name ) AS A
GROUP BY A.sku ) Attributes
WHERE Attributes.sku = PD.sku
FOR xml path('')), 1, 1, '') AS CustomValues
FROM [product_details] PD) AS productDetails
ORDER BY productDetails.sku
Try this:
SELECT DISTINCT P.SKU,P.UPC,Details,P.[Weight],P.[Color],
STUFF((SELECT ',' + Custom_value
from Custom_tbl
where SKU=P.SKU FOR XML PATH('')),1,1,'') AS Customvalues
FROM Product_table P
You can use GROUP BY in Custom_tbl table per SKU and use CASE WHEN to get individual custom values
SELECT
SKU,
ISNULL(CASE WHEN Custom_name = 'Pickup' THEN Custom_value END,'') + ','
ISNULL(CASE WHEN Custom_name = 'eligible' THEN Custom_value END,'') + ','
ISNULL(CASE WHEN Custom_name = 'map' THEN Custom_value END,'') as CustomValues
FROM Custom_tbl
WHERE Custom_name IN ('Pickup','eligible','map')
GROUP BY SKU
Now just join this with the primary table per sku
SELECT P.SKU, UPC, DETAILS, WEIGHT, COLOR, ISNULL(C.CustomValues,'') as CustomValues
FROM Product_table P
LEFT JOIN(
SELECT
SKU,
ISNULL(CASE WHEN Custom_name = 'Pickup' THEN Custom_value END,'') + ','
ISNULL(CASE WHEN Custom_name = 'eligible' THEN Custom_value END,'') + ','
ISNULL(CASE WHEN Custom_name = 'map' THEN Custom_value END,'') as CustomValues
FROM Custom_tbl
WHERE Custom_name IN ('Pickup','eligible','map')
GROUP BY SKU
)C ON P.sku = C.sku
DECLARE #Product_Table AS TABLE (SKU INT, UPC INt, Details VARCHAR(100), Weight INT, Color VARCHAR(100))
DECLARE #Custom_Tbl AS TABLE (SKU INT, Custom_Name VARCHAR(100), Custom_Value VARCHAR(100))
INSERT INTO #Product_Table VALUES (223,111,'TShirt',25,'White'),(224,114,'Pants',25,'Black')
,(225,115,'Abc',29,'Yellow'),(230,116,'XyX',23,'Pink'),(226,117,'AXYz',25,'Red')
,(226,118,'Abdc',26,'White'),(228,119,'Abcr',20,'Pink'),(229,120,'Abcy',22,'Green')
INSERT INTO #Custom_Tbl VALUES (223,'Pickup','true'),(223,'eligible','false'),(223,'size','medium')
,(223,'map','red'),(224,'pickup','false'),(224,'eligible','false'),(224,'map','green')
,(225,'Pickup','true'),(225,'eligible','true'),(225,'size','large'),(225,'department','001')
,(225,'availability','true'),(226,'Pickup','true'),(226,'size','large'),(226,'map','blue')
,(226,'availability','true'),(229,'eligible','true')
;WITH cteCustomNames AS (
SELECT *
FROM
(VALUES ('Pickup',1),('eligible',2),('map',3)) t(Custom_name,StringOrder)
)
, cteProductRowNum AS (
SELECT
p.*
,RowNum = ROW_NUMBER() OVER (PARTITION BY p.SKU ORDER BY Weight)
FROM
#Product_Table p
)
SELECT * , STUFF(
(SELECT ',' + ISNULL(Custom_value,'')
FROM
cteCustomNames cn
LEFT JOIN #Custom_Tbl c
ON cn.Custom_name = c.Custom_Name
AND c.SKU = p.SKU
ORDER BY
cn.StringOrder
FOR XML PATH(''))
,1,1,'')
FROM
cteProductRowNum p
WHERE
p.RowNum = 1
So you acutally have a few tricky parts to your query. First you are showing commas for every position even if that position doesn't exit. That takes either a cross or a left join to pull off. You are showing results that would be 1 row for each SKU but in your Products Table you are showing multiple products per SKU so you need a ranking function to determine what row you want. Anyway, here is a way to accomplish it all. Like the others I too recommend STUFF() with FOR XML for the concatenation. Oh and you mention you want the strings in a particular order.
Please help me to solve my below query -
I have the following data in my table-
Agent Variable Chandigarh NewDelhi
ABC Leads 102.00 10
ABC TotalTime 10.52 1
ABC RPH 22.79 22
ABC TotalRev 239.70 23
XYZ Leads 14.00 14
XYZ TotalTime 1.52 1
XYZ RPH 21.64 21
XYZ TotalRev 32.90 32
I want the solution like this
Agent Chandigarh_Leads Chandigarh_TotalTime Chandigarh_RPH Chandigarh_RPH_TotalRev NewDelhi_Leads .......
ABC 102.00 10.52 22.79 239.70 10 .......
XYZ 14 1.52 21.64 32.90 14 ............
FYI, I can have more states in columns, it has no limits it may be 10 or 20 or 5 etc. So i need result dynamic query. Please help me, is it possible without static query?
Dynamic SQL + pivoting:
DECLARE #sql nvarchar(max),
#columns nvarchar(max),
#col_to_cast nvarchar(max),
#col_unpvt nvarchar(max)
--This will give:
--,[Chandigarh_Leads],[Chandigarh_RPH]....[NewDelhi_TotalRev],[NewDelhi_TotalTime]
SELECT #columns = COALESCE(#columns,'')+',['+name+'_'+Variable +']'
FROM (
SELECT DISTINCT Variable
FROM #yourtable) v
CROSS JOIN (
SELECT name
FROM sys.columns
WHERE object_id = OBJECT_ID(N'#yourtable')
AND name not in ('Agent', 'Variable')
) c
ORDER BY c.name, v.Variable
--As columns while unpivoting must be same type we need to cast them in same datattype:
--This will give
--,CAST([Chandigarh] as float) as [Chandigarh],CAST([NewDelhi] as float) as [NewDelhi]
SELECT #col_to_cast = COALESCE(#col_to_cast,'')+',CAST(' + QUOTENAME(name)+ ' as float) as '+ QUOTENAME(name)
#col_unpvt = COALESCE(#col_unpvt,'') + ','+ QUOTENAME(name)
FROM sys.columns
WHERE object_id = OBJECT_ID(N'#yourtable')
AND name not in ('Agent', 'Variable')
SELECT #sql = N'
SELECT *
FROM (
SELECT Agent,
[Columns]+''_''+Variable as ColName,
[Values] as ColVal
FROM (
SELECT Agent,
Variable'+#col_to_cast+'
FROM #yourtable
) p
UNPIVOT (
[Values] FOR [Columns] IN ('+STUFF(#col_unpvt,1,1,'')+')
) unpvt
) t
PIVOT (
MAX(ColVal) FOR ColName IN ('+STUFF(#columns,1,1,'')+')
) pvt'
EXEC sp_executesql #sql
Output:
Agent Chandigarh_Leads Chandigarh_RPH Chandigarh_TotalRev Chandigarh_TotalTime NewDelhi_Leads NewDelhi_RPH NewDelhi_TotalRev NewDelhi_TotalTime
ABC 102 22,79 239,7 10,52 10 22 23 1
XYZ 14 21,64 32,9 1,52 14 21 32 1