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 a requirement where my query will return output something like this:
PermissionType IsAllowed
-------------------------
IsEdit | 1
IsDelete | 0
isRemove | 1
isPrint | 1
isReport | 0
-- | -
-- | -
-- | -
--------------------------
These rows can be dynamic depending upon the filter criteria I will pass.
So now I want to convert the above resultset to the following:
IsEdit | IsDelete | IsRemove | IsPrint | IsReport | -- | -- | --
--------------------------------------------------------------------
1 | 0 | 1 | 1 | 0 | - | - | -
I tried to use the pivot here but it asks the Column Names to be pivoted into the output and this is dynamic in my case, also it needed an aggregate function for FOR but I don't have any calculation in my case.
Anyone please help me on this.
Then try this Dynamic sql
IF OBJECT_ID('dbo.temp')IS NOT NULL
DROP TABLE temp
;WITH Cte(PermissionType, IsAllowed)
AS
(
SELECT 'IsEdit' , 1 UNION ALL
SELECT 'IsDelete' , 0 UNION ALL
SELECT 'isRemove' , 1 UNION ALL
SELECT 'isPrint' , 1 UNION ALL
SELECT 'isReport' , 0
)
SELECT *,ROW_NUMBER()OVER(ORDER BY (SELECT 1)) AS Seq INTO
temp FROM Cte
DECLARE #Sql nvarchar(max),
#Sqlcol nvarchar(max),
#ISNULLSqlcol nvarchar(max)
SELECT #Sqlcol=STUFF((SELECT ', '+QUOTENAME(PermissionType)
FROM temp ORDER BY Seq FOR XML PATH ('')),1,1,'')
SELECT #ISNULLSqlcol=STUFF((SELECT ', '+'MAX('+QUOTENAME(PermissionType) +') AS '+QUOTENAME(PermissionType)
FROM temp ORDER BY Seq FOR XML PATH ('')),1,1,'')
SET #Sql='
SELECT '+#ISNULLSqlcol+'
FROM(
SELECT * FROM temp
)AS SRC
PIVOT
(
MAX(IsAllowed) FOR PermissionType IN ('+#Sqlcol+')
) AS PVT '
PRINT #Sql
EXEC (#Sql)
IsEdit IsDelete isRemove isPrint isReport
--------------------------------------------------
1 0 1 1 0
With pivot and dynamic sql you can create a query that will include a different number of columns:
if OBJECT_ID('Test') is not null
drop table [dbo].[Test]
CREATE TABLE [dbo].[Test](PermissionType varchar(20), IsAllowed int)
insert into [dbo].[Test] values
('IsEdit' , 1)
,('IsDelete', 0)
,('isRemove', 1)
,('isPrint' , 1)
,('isReport', 0)
--this variable holds all the dates that will become column names
declare #permissionTypes nvarchar(max) = ''
--this variable contains the TSQL dinamically generated
declare #sql nvarchar(max) = ''
select #permissionTypes = #permissionTypes + ', ' + quotename(PermissionType)
from [dbo].[Test]
set #permissionTypes = RIGHT(#permissionTypes, len(#permissionTypes)-2)
set #sql = concat(
'select *
from [dbo].[Test]
pivot
(
max(isallowed)
for PermissionType in (', #permissionTypes, ')
) piv '
)
exec(#sql)
Result:
Adding a new row:
insert into [dbo].[Test] values
('IsNew' , 1)
Causes a new column to be created:
Currently building a SELECT statement in SQL Server 2008 but would like to make this SELECT statement dynamic, so the columns can be defined based on values in a table. I heard about pivot table and cursors, but seems kind of hard to understand at my current level, here is the code;
DECLARE #date DATE = null
IF #date is null
set # date = GETDATE() as DATE
SELECT
Name,
value1,
value2,
value3,
value4
FROM ref_Table a
FULL OUTER JOIN (
SELECT
PK_ID ID,
sum(case when FK_ContainerType_ID = 1 then 1 else null) Box,
sum(case when FK_ContainerType_ID = 2 then 1 else null) Pallet,
sum(case when FK_ContainerType_ID = 3 then 1 else null) Bag,
sum(case when FK_ContainerType_ID = 4 then 1 else null) Drum
from
Packages
WHERE
#date between PackageStart AND PackageEnd
group by PK_ID ) b on a.Name = b.ID
where
Group = 0
The following works great for me , but PK_Type_ID and the name of the column(PackageNameX,..) are hard coded, I need to be dynamic and it can build itself based on present or futures values in the Package table.
Any help or guidance on the right direction would be greatly appreciated...,
As requested
ref_Table (PK_ID, Name)
1, John
2, Mary
3, Albert
4, Jane
Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
1 , 1, 4, 1JAN2014, 30JAN2014
2 , 2, 3, 1JAN2014, 30JAN2014
3 , 3, 2, 1JAN2014, 30JAN2014
4 , 4, 1, 1JAN2014, 30JAN2014
ContainerType (PK_ID, Type)
1, Box
2, Pallet
3, Bag
4, Drum
and the result should look like this;
Name Box Pallet Bag Drum
---------------------------------------
John 1
Mary 1
Albert 1
Jane 1
The following code like I said works great, the issue is the Container table is going to grow and I need to replicated the same report without hard coding the columns.
What you need to build is called a dynamic pivot. There are plenty of good references on Stack if you search out that term.
Here is a solution to your scenario:
IF OBJECT_ID('tempdb..##ref_Table') IS NOT NULL
DROP TABLE ##ref_Table
IF OBJECT_ID('tempdb..##Packages') IS NOT NULL
DROP TABLE ##Packages
IF OBJECT_ID('tempdb..##ContainerType') IS NOT NULL
DROP TABLE ##ContainerType
SET NOCOUNT ON
CREATE TABLE ##ref_Table (PK_ID INT, NAME NVARCHAR(50))
CREATE TABLE ##Packages (PK_ID INT, FK_ref_Table_ID INT, FK_ContainerType_ID INT, PackageStartDate DATE, PackageEndDate DATE)
CREATE TABLE ##ContainerType (PK_ID INT, [Type] NVARCHAR(50))
INSERT INTO ##ref_Table (PK_ID,NAME)
SELECT 1,'John' UNION
SELECT 2,'Mary' UNION
SELECT 3,'Albert' UNION
SELECT 4,'Jane'
INSERT INTO ##Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
SELECT 1,1,4,'2014-01-01','2014-01-30' UNION
SELECT 2,2,3,'2014-01-01','2014-01-30' UNION
SELECT 3,3,2,'2014-01-01','2014-01-30' UNION
SELECT 4,4,1,'2014-01-01','2014-01-30'
INSERT INTO ##ContainerType (PK_ID, [Type])
SELECT 1,'Box' UNION
SELECT 2,'Pallet' UNION
SELECT 3,'Bag' UNION
SELECT 4,'Drum'
DECLARE #DATE DATE, #PARAMDEF NVARCHAR(MAX), #COLS NVARCHAR(MAX), #SQL NVARCHAR(MAX)
SET #DATE = '2014-01-15'
SET #COLS = STUFF((SELECT DISTINCT ',' + QUOTENAME(T.[Type])
FROM ##ContainerType T
FOR XML PATH, TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #SQL = 'SELECT [Name], ' + #COLS + '
FROM (SELECT [Name], [Type], 1 AS Value
FROM ##ref_Table R
JOIN ##Packages P ON R.PK_ID = P.FK_ref_Table_ID
JOIN ##ContainerType T ON P.FK_ContainerType_ID = T.PK_ID
WHERE #DATE BETWEEN P.PackageStartDate AND P.PackageEndDate) X
PIVOT (COUNT(Value) FOR [Type] IN (' + #COLS + ')) P
'
PRINT #COLS
PRINT #SQL
SET #PARAMDEF = '#DATE DATE'
EXEC SP_EXECUTESQL #SQL, #PARAMDEF, #DATE=#DATE
Output:
Name Bag Box Drum Pallet
Albert 0 0 0 1
Jane 0 1 0 0
John 0 0 1 0
Mary 1 0 0 0
Static Query:
SELECT [Name],[Box],[Pallet],[Bag],[Drum] FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( [Box],[Pallet],[Bag],[Drum])
) AS PivotTable
) AS Main
ORDER BY RFID
Dynamic Query:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + [Type] + ']'
FROM ContanerType
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT [Name],' + #columnList + ' FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( ' + #columnList + ')
) AS PivotTable
) AS Main
ORDER BY RFID;'
EXEC sp_executesql #pivotsql
Following my tutorial below will help you to understand the PIVOT functionality:
We write sql queries in order to get different result sets like full, partial, calculated, grouped, sorted etc from the database tables. However sometimes we have requirements that we have to rotate our tables. Sounds confusing?
Let's keep it simple and consider the following two screen grabs.
SQL Table:
Expected Results:
Wow, that's look like a lot of work! That is a combination of tricky sql, temporary tables, loops, aggregation......, blah blah blah
Don't worry let's keep it simple, stupid(KISS).
MS SQL Server 2005 and above has a function called PIVOT. It s very simple to use and powerful. With the help of this function we will be able to rotate sql tables and result sets.
Simple steps to make it happen:
Identify all the columns those will be part of the desired result set.
Find the column on which we will apply aggregation(sum,ave,max,min etc)
Identify the column which values will be the column header.
Specify the column values mentioned in step3 with comma separated and surrounded by square brackets.
So, if we now follow above four steps and extract information from the above sales table, it will be as below:
Year, Month, SalesAmount
SalesAmount
Month
[Jan],[Feb] ,[Mar] .... etc
We are nearly there if all the above steps made sense to you so far.
Now we have all the information we need. All we have to do now is to fill the below template with required information.
Template:
Our SQL query should look like below:
SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( [Jan],[Feb] ,[Mar],
[Apr],[May],[Jun] ,[Jul],
[Aug],[Sep] ,[Oct],[Nov] ,[Dec])
) AS PivotTable;
In the above query we have hard coded the column names. Well it's not fun when you have to specify a number of columns.
However, there is a work arround as follows:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + SalesMonth + ']'
FROM Sales
GROUP BY SalesMonth
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( ' + #columnList +' )
) AS PivotTable;'
EXEC sp_executesql #pivotsql
Hopefully this tutorial will be a help to someone somewhere.
Enjoy coding.
I have a data as below:
Table
country date value
------------------------------------------------------
test1 5/1/2008 500
test1 5/7/2008 200
test1 5/8/2008 300
test1 7/1/2008 100
test1 7/2/2008 100
test2 6/1/2008 100
And I want a result as below:
Result
-----------
countryName May-08 Jun-08 July-08
test1 1000 - 200
test2 - 100
This is adapted from T-SQL Pivot? Possibility of creating table columns from row values
You can see it working here: http://sqlfiddle.com/#!3/7b8c0/28
I think you might need to fiddle around with the column ordering
-- Static PIVOT
SELECT *
FROM (SELECT country,
CONVERT(char(3), date, 0) + '-' +
RIGHT(CONVERT(varchar, YEAR(date)), 2) AS date,
value
FROM country) AS D
PIVOT(SUM(value) FOR date IN([May-08],[Jun-08],[Jul-08])) AS P;
GO
-- Dynamic PIVOT
DECLARE #T AS TABLE(y INT NOT NULL PRIMARY KEY);
DECLARE
#cols AS NVARCHAR(MAX),
#y AS INT,
#sql AS NVARCHAR(MAX)
SELECT #cols = STUFF(
(SELECT N',' + QUOTENAME(y) AS [text()]
FROM (SELECT DISTINCT CONVERT(char(3), date, 0) + '-' +
RIGHT(CONVERT(varchar, YEAR(date)), 2) AS y
FROM Country
) AS Y
ORDER BY y desc
FOR XML PATH('')),
1, 1, N'')
-- Construct the full T-SQL statement
-- and execute dynamically
SET #sql = N'SELECT *
FROM (SELECT country, CONVERT(char(3), date, 0) + ''-'' +
RIGHT(CONVERT(varchar, YEAR(date)), 2) AS date, value
FROM Country) AS D
PIVOT(SUM(value) FOR date IN(' + #cols + N')) AS P;'
EXEC sp_executesql #sql
You have to use a rather complex query for that, using a LOOP it think.
For creating dynamic column names look at this post: https://stackoverflow.com/a/10926106/1321564
With sql server you have some advantages: https://stackoverflow.com/a/5638042/1321564