Answered by:
Display data where GROUP BY column are the column names

Question
-
I don't have much experience with dynamic sql, and I have no idea where to begin.
Table [Sales] has data that looks like the following:
Name Date Hr Min Amt Joe 20150320 08 00 5 Joe 20150320 08 15 3 Carl 20150320 09 30 1 Carl 20150320 09 45 2 Ray 20150320 13 00 8 Ray 20150320 13 30 6
A simple GROUP BY [Name], [Date], [Hr] would display the total by hour for each salesman.
Assuming the sample above is actually all the data in the table, how would the dynamic sql look like so that the column names are actually the column [Hour]? In the case above, the resultset would be composed of 4 columns: [Name], [Date], [08], [09], [13].
Thanks.
VM
Friday, March 20, 2015 8:18 PM
Answers
-
So something like this... (add additional COUNT statements per hour)
Select * into #DEMOTABLE from ( Values ( 'Joe' , '20150320' , '8', '00', 5 ) , ( 'Joe' , '20150320' , '8', '15', 3 ) , ( 'Carl' , '20150320' , '9', '30', 1 ) , ( 'Carl' , '20150320' , '9', '45', 2 ) , ( 'Ray' , '20150320' , '13', '00', 8 ) , ( 'Ray' , '20150320' , '13', '30', 6 ) ) as VT(Name, Date, Hr, Min, Amt) Select Name, "date" , Count(case when HR between 0 and 7 then 'x' else null end) as Midnignt_to_7 , Count(case when HR = '8' then 'x' else null end) as H8 , Count(case when HR = '9' then 'x' else null end) as H9 , Count(case when HR = '13' then 'x' else null end) as H13 , Count(case when HR between '19' and '23' then 'x' else null end) as H7_to_Midnight from #DEMOTABLE group by Name, Date
Output would look like: (minus the columns getting out of alignment)
Name date Midnignt_to_7 H8 H9 H13 H7_to_Midnight Carl 20150320 0 0 2 0 0 Joe 20150320 0 2 0 0 0 Ray 20150320 0 0 0 2 0
- Edited by johnqflorida Saturday, March 21, 2015 2:40 PM
- Proposed as answer by Charlie Liao Tuesday, March 24, 2015 3:17 PM
- Marked as answer by Charlie Liao Friday, April 3, 2015 1:26 AM
Saturday, March 21, 2015 2:24 PM -
And then finally... A way to do both... First query lists all business hours (you could modify to show all 24 hours), and second relatively simple dynamic query can show just the hours where there were sales. This could be rolled into a single query too, of course, but I bet after a week of realizing the columns in the dynamic query change every day, most people would want the more static list... so you're better off having it done in advance.
Select * into #DEMOTABLE from -- select * from == Drop table #DEMOTABLE ( Values ( 'Joe' , '20150320' , '8', '00', 5 ) , ( 'Joe' , '20150320' , '8', '15', 3 ) , ( 'Carl' , '20150320' , '9', '30', 1 ) , ( 'Carl' , '20150320' , '9', '45', 2 ) , ( 'Ray' , '20150320' , '13', '00', 8 ) , ( 'Ray' , '20150320' , '13', '30', 6 ) ) as VT(Name, Date, Hr, Min, Amt) Select Name, "date" , Count(case when HR between 0 and 7 then 'x' else null end) as HM_to_7A , Count(case when HR = '8' then 'x' else null end) as H8 , Count(case when HR = '9' then 'x' else null end) as H9 , Count(case when HR = '10' then 'x' else null end) as H10 , Count(case when HR = '11' then 'x' else null end) as H11 , Count(case when HR = '12' then 'x' else null end) as H12 , Count(case when HR = '13' then 'x' else null end) as H13 , Count(case when HR = '14' then 'x' else null end) as H14 , Count(case when HR = '15' then 'x' else null end) as H15 , Count(case when HR = '16' then 'x' else null end) as H16 , Count(case when HR = '17' then 'x' else null end) as H17 , Count(case when HR = '18' then 'x' else null end) as H18 , Count(case when HR = '19' then 'x' else null end) as H19 , Count(case when HR between '19' and '23' then 'x' else null end) as H7P_to_M into #DEMOSUMMARY from #DEMOTABLE group by Name, Date
Select * from #DEMOSUMMARY
Declare @DynSQL as NVarChar(Max) Set @DynSQL = 'Select Name, Date ' If Exists (Select 'x' from #DemoSUmmary where HM_to_7A > 0) Set @DynSQL = @DynSQL + ', HM_to_7A' If Exists (Select 'x' from #DemoSummary where H8 > 0) Set @DynSQL = @DynSQL + ', H8' If Exists (Select 'x' from #DemoSummary where H9 > 0) Set @DynSQL = @DynSQL + ', H9' If Exists (Select 'x' from #DemoSummary where H10 > 0) Set @DynSQL = @DynSQL + ', H10' If Exists (Select 'x' from #DemoSummary where H11 > 0) Set @DynSQL = @DynSQL + ', H11' If Exists (Select 'x' from #DemoSummary where H12 > 0) Set @DynSQL = @DynSQL + ', H12' If Exists (Select 'x' from #DemoSummary where H13 > 0) Set @DynSQL = @DynSQL + ', H13' If Exists (Select 'x' from #DemoSummary where H14 > 0) Set @DynSQL = @DynSQL + ', H14' If Exists (Select 'x' from #DemoSummary where H15 > 0) Set @DynSQL = @DynSQL + ', H15' If Exists (Select 'x' from #DemoSummary where H16 > 0) Set @DynSQL = @DynSQL + ', H16' If Exists (Select 'x' from #DemoSummary where H17 > 0) Set @DynSQL = @DynSQL + ', H17' If Exists (Select 'x' from #DemoSummary where H18 > 0) Set @DynSQL = @DynSQL + ', H18' If Exists (Select 'x' from #DemoSummary where H7P_to_M > 0) Set @DynSQL = @DynSQL + ', H7P_to_M ' Set @DynSQL = @DynSQL + ' From #DemoSummary' Select @DYNSQL Execute sp_ExecuteSQL @DynSQL
- Marked as answer by Charlie Liao Friday, April 3, 2015 1:26 AM
Sunday, March 22, 2015 8:24 PM
All replies
-
what you are looking for is dynamic pivot...
refer this link that explains how it can be done. http://sqlhints.com/2014/03/18/dynamic-pivot-in-sql-server
Hope it Helps!!
Friday, March 20, 2015 8:27 PM -
I believe this is what you are after based on your description. Here it is with the temp table and data (include that next time please)
if OBJECT_ID('tempdb..#temp') is not null drop table #temp
CREATE TABLE #temp
(
name varchar(10),
[Date] varchar(10),
hr int,
[Min] int,
Amt int
)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Joe','20150320',08,00,5)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Joe','20150320',08,15,3)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Carl','20150320',09,30,1)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Carl','20150320',09,45,2)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Ray','20150320',13,00,8)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Ray','20150320',13,30,6)
declare @pivot nvarchar(max)
set @pivot = ''
select @pivot = @pivot + ',[' + convert(nvarchar(2000), t.hr) + ']'
from
(
select distinct
hr
from #temp
) t
order by hr asc
if(LEN(@pivot) != 0)
begin
select @pivot = right(@pivot, len(@pivot) - 1)
end
declare @sql nvarchar(max) =
'
select
*
from
(
select
x.name,
x.[Date],
x.hr,
x.[Min],
x.Amt
from #temp x
) row
pivot
(
max(hr)
for hr in (' + @pivot + ')
) col
'
--select @sql
exec sp_executesql @sql
Friday, March 20, 2015 8:33 PM -
Try this:
CREATE TABLE #table (name VARCHAR(20), date DATE, hr CHAR(2), min CHAR(20), amt INT) INSERT INTO #table (name, date, hr, min, amt) VALUES ('Joe ', '20150320', '08', '00', 5),('Joe ', '20150320', '08', '15', 3),('Carl', '20150320', '09', '30', 1), ('Carl', '20150320', '09', '45', 2),('Ray ', '20150320', '13', '00', 8),('Ray ', '20150320', '13', '30', 6), ('Patrick', '20150320', '14', '30', 6),('Patrick', '20150320', '15', '30', 6),('Patrick', '20150320', '16', '30', 6) DECLARE @dSQL NVARCHAR(MAX), @pivotCols NVARCHAR(MAX) ;WITH base AS ( SELECT CAST('['+hr+']' AS NVARCHAR(MAX)) AS string, ROW_NUMBER() OVER (ORDER BY CAST(hr AS INT) DESC) AS seq FROM #table GROUP BY hr ), rCTE AS ( SELECT string, seq FROM base WHERE seq = 1 UNION ALL SELECT a.string +',' + r.string, a.seq FROM base a INNER JOIN rCTE r ON r.seq + 1 = a.seq ) SELECT @pivotCols = string FROM rCTE WHERE seq = (SELECT MAX(seq) FROM rCTE) SET @dSQL = 'SELECT name, date, '+@pivotCols+' FROM ( SELECT name, date, amt, hr FROM #table ) s PIVOT ( SUM(amt) FOR hr IN ('+@pivotCols+') ) p GROUP BY name, date, '+@pivotCols EXEC sp_executeSQL @dSQL DROP TABLE #table
Don't forget to mark helpful posts, and answers. It helps others to find relevant posts to the same question.
- Edited by Patrick Hurst Friday, March 20, 2015 9:04 PM
Friday, March 20, 2015 8:59 PM -
I had seen that link. The problem is that it doesn't GROUP BY.
VM
Friday, March 20, 2015 8:59 PM -
Ok, I see now. This should do it
--Note: GENERAL
if OBJECT_ID('tempdb..#temp') is not null drop table #temp
CREATE TABLE #temp
(
name varchar(10),
[Date] varchar(10),
hr int,
[Min] int,
Amt int
)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Joe','20150320',08,00,5)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Joe','20150320',08,15,3)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Carl','20150320',09,30,1)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Carl','20150320',09,45,2)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Ray','20150320',13,00,8)
insert into #temp(name, [Date], hr, [Min], Amt) values ('Ray','20150320',13,30,6)
declare @pivot nvarchar(max)
set @pivot = ''
select @pivot = @pivot + ',[' + convert(nvarchar(2000), t.hr) + ']'
from
(
select distinct
hr
from #temp
) t
order by hr asc
if(LEN(@pivot) != 0)
begin
select @pivot = right(@pivot, len(@pivot) - 1)
end
declare @sql nvarchar(max) =
'
select
*
from
(
select
x.name,
x.[Date],
x.hr
from #temp x
) row
pivot
(
sum(hr)
for hr in (' + @pivot + ')
) col
'
--select @sql
exec sp_executesql @sql
Friday, March 20, 2015 9:24 PM -
As a suggestion, after you figure this out: This isn't a scenario with endless possibilities, there are only 24 hours in a day, and chances are, you're looking at only 8 to 10 hours in a workday, so you could also do a pivot (the static kind, a.k.a. not dynamic) with the hours in the day you care about (in this particular example there would be empty hours).
EDIT: Also, see this link on a similar topic and Erland's answer: That often, when the number of pivoted columns is smaller, that it's often far more desirable to not use the PIVOT statement at all. Also, this approach would make it easier to have a catch-all "Other" column, perhaps "Midnight to 7 a.m.", 8, 9, 10... 5, 6, 7, "8pm to midnight" category. https://social.msdn.microsoft.com/Forums/sqlserver/en-US/c5fd56fa-a508-4bbd-8cfd-27bb9c925172/sql-server-pivot-sum-by-day-grand-total?forum=transactsql (Erland has articles on other reasons to dodge the Pivot statement when you can, visit his site).
- Edited by johnqflorida Saturday, March 21, 2015 12:54 PM Added link
Saturday, March 21, 2015 12:48 PM -
So something like this... (add additional COUNT statements per hour)
Select * into #DEMOTABLE from ( Values ( 'Joe' , '20150320' , '8', '00', 5 ) , ( 'Joe' , '20150320' , '8', '15', 3 ) , ( 'Carl' , '20150320' , '9', '30', 1 ) , ( 'Carl' , '20150320' , '9', '45', 2 ) , ( 'Ray' , '20150320' , '13', '00', 8 ) , ( 'Ray' , '20150320' , '13', '30', 6 ) ) as VT(Name, Date, Hr, Min, Amt) Select Name, "date" , Count(case when HR between 0 and 7 then 'x' else null end) as Midnignt_to_7 , Count(case when HR = '8' then 'x' else null end) as H8 , Count(case when HR = '9' then 'x' else null end) as H9 , Count(case when HR = '13' then 'x' else null end) as H13 , Count(case when HR between '19' and '23' then 'x' else null end) as H7_to_Midnight from #DEMOTABLE group by Name, Date
Output would look like: (minus the columns getting out of alignment)
Name date Midnignt_to_7 H8 H9 H13 H7_to_Midnight Carl 20150320 0 0 2 0 0 Joe 20150320 0 2 0 0 0 Ray 20150320 0 0 0 2 0
- Edited by johnqflorida Saturday, March 21, 2015 2:40 PM
- Proposed as answer by Charlie Liao Tuesday, March 24, 2015 3:17 PM
- Marked as answer by Charlie Liao Friday, April 3, 2015 1:26 AM
Saturday, March 21, 2015 2:24 PM -
And then finally... A way to do both... First query lists all business hours (you could modify to show all 24 hours), and second relatively simple dynamic query can show just the hours where there were sales. This could be rolled into a single query too, of course, but I bet after a week of realizing the columns in the dynamic query change every day, most people would want the more static list... so you're better off having it done in advance.
Select * into #DEMOTABLE from -- select * from == Drop table #DEMOTABLE ( Values ( 'Joe' , '20150320' , '8', '00', 5 ) , ( 'Joe' , '20150320' , '8', '15', 3 ) , ( 'Carl' , '20150320' , '9', '30', 1 ) , ( 'Carl' , '20150320' , '9', '45', 2 ) , ( 'Ray' , '20150320' , '13', '00', 8 ) , ( 'Ray' , '20150320' , '13', '30', 6 ) ) as VT(Name, Date, Hr, Min, Amt) Select Name, "date" , Count(case when HR between 0 and 7 then 'x' else null end) as HM_to_7A , Count(case when HR = '8' then 'x' else null end) as H8 , Count(case when HR = '9' then 'x' else null end) as H9 , Count(case when HR = '10' then 'x' else null end) as H10 , Count(case when HR = '11' then 'x' else null end) as H11 , Count(case when HR = '12' then 'x' else null end) as H12 , Count(case when HR = '13' then 'x' else null end) as H13 , Count(case when HR = '14' then 'x' else null end) as H14 , Count(case when HR = '15' then 'x' else null end) as H15 , Count(case when HR = '16' then 'x' else null end) as H16 , Count(case when HR = '17' then 'x' else null end) as H17 , Count(case when HR = '18' then 'x' else null end) as H18 , Count(case when HR = '19' then 'x' else null end) as H19 , Count(case when HR between '19' and '23' then 'x' else null end) as H7P_to_M into #DEMOSUMMARY from #DEMOTABLE group by Name, Date
Select * from #DEMOSUMMARY
Declare @DynSQL as NVarChar(Max) Set @DynSQL = 'Select Name, Date ' If Exists (Select 'x' from #DemoSUmmary where HM_to_7A > 0) Set @DynSQL = @DynSQL + ', HM_to_7A' If Exists (Select 'x' from #DemoSummary where H8 > 0) Set @DynSQL = @DynSQL + ', H8' If Exists (Select 'x' from #DemoSummary where H9 > 0) Set @DynSQL = @DynSQL + ', H9' If Exists (Select 'x' from #DemoSummary where H10 > 0) Set @DynSQL = @DynSQL + ', H10' If Exists (Select 'x' from #DemoSummary where H11 > 0) Set @DynSQL = @DynSQL + ', H11' If Exists (Select 'x' from #DemoSummary where H12 > 0) Set @DynSQL = @DynSQL + ', H12' If Exists (Select 'x' from #DemoSummary where H13 > 0) Set @DynSQL = @DynSQL + ', H13' If Exists (Select 'x' from #DemoSummary where H14 > 0) Set @DynSQL = @DynSQL + ', H14' If Exists (Select 'x' from #DemoSummary where H15 > 0) Set @DynSQL = @DynSQL + ', H15' If Exists (Select 'x' from #DemoSummary where H16 > 0) Set @DynSQL = @DynSQL + ', H16' If Exists (Select 'x' from #DemoSummary where H17 > 0) Set @DynSQL = @DynSQL + ', H17' If Exists (Select 'x' from #DemoSummary where H18 > 0) Set @DynSQL = @DynSQL + ', H18' If Exists (Select 'x' from #DemoSummary where H7P_to_M > 0) Set @DynSQL = @DynSQL + ', H7P_to_M ' Set @DynSQL = @DynSQL + ' From #DemoSummary' Select @DYNSQL Execute sp_ExecuteSQL @DynSQL
- Marked as answer by Charlie Liao Friday, April 3, 2015 1:26 AM
Sunday, March 22, 2015 8:24 PM