Reputation: 121
i really want to asking about pivot table in sql server. i have trying to do some query where the structure is like this.
DECLARE @cols AS NVARCHAR(MAX)
DECLARE @query AS NVARCHAR(MAX)
DECLARE @tanggal_awal date
DECLARE @tanggal_akhir date
DECLARE @print nvarchar(MAX)
CREATE TABLE #datatable
(
product_id int,
product_date date,
product_ammount int
)
SET @tanggal_awal = convert(date,'02-01-2017')
SET @tanggal_akhir = convert(date,'02-27-2017')
insert into #datatable (product_id,product_date,product_ammount) VALUES
(1,getdate(),100),
(1,getdate(),900),
(2,dateadd(DD,-1,getdate()),400),
(3,DATEADD(DD,4,getdate()),300),
(1,dateadd(DD,4,getdate()),200),
(2,dateadd(DD,2,getdate()),700),
(4,dateadd(DD,-3,getdate()),1000)
--SELECT * FROM @datatable
;WITH CTE (datelist,maxdate) AS
(
select min(@tanggal_awal) datelist, max(product_date) maxdate
from #datatable
union all
select dateadd(dd, 1, datelist), @tanggal_akhir
from cte
where datelist < maxdate
) SELECT c.datelist
into #temp
from cte c
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(convert(CHAR(10), datelist, 120))
from #temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--SELECT d.datelist, b.product_id, SUM(b.product_ammount)
-- FROM #temp d left join #datatable b on
-- d.datelist between @tanggal_awal and @tanggal_akhir
-- AND d.datelist = b.product_date
-- GROUP BY b.product_id, d.datelist
--select b.product_id, d.datelist,
-- sum(b.product_ammount) PivotDate
-- from #datatable b
-- left join #temp d
-- on d.datelist between @tanggal_awal and @tanggal_akhir
-- and d.datelist = b.product_date
--group by b.product_id, d.datelist
--order by b.product_id
SET @query = 'SELECT product_id, '+@cols+' FROM
(
select b.product_id, d.datelist, convert(CHAR(10), datelist, 120) PivotDate
from #datatable b
left join #temp d
on d.datelist between @tanggal_awal and @tanggal_akhir
and d.datelist = b.product_date
) x
pivot
(
count(datelist)
for PivotDate in (' +@cols+ ')
) p'
EXECUTE sp_executesql @query ,N'@tanggal_awal DATE, @tanggal_akhir DATE', @tanggal_awal,@tanggal_akhir
GO
drop table #temp
go
drop table #datatable
the result from this query is mostly count of the 'product_id' in the respective date. however, i want to get sum of the total based on product_ammount like this.
-------------------------------------
|product_id| 2017-01-02 | 2017-02-02|
-------------------------------------
|1 | 0 | 1000 |
-------------------------------------
|2 | 900 | 0 |
-------------------------------------
|3 | 700 | 0 |
-------------------------------------
can anyone give me the best way to handle join and pivot together, especially for this case ? thank you very much.
--Update after the query from Shakeer Mirza, the result is like this.
can we group it ?
Upvotes: 3
Views: 405
Reputation: 5110
Try this below. I think you need change your Pivot to SUM
of product_ammount
and the qry X
to have product_ammount
in Select
SET @query = 'SELECT product_id, '+@cols+' FROM
(
select b.product_id, convert(CHAR(10), datelist, 120) PivotDate, product_ammount
from #datatable b
left join #temp d
on d.datelist between @tanggal_awal and @tanggal_akhir
and d.datelist = b.product_date
) x
pivot
(
sum(product_ammount)
for PivotDate in (' +@cols+ ')
) p'
EXECUTE sp_executesql @query ,N'@tanggal_awal DATE, @tanggal_akhir DATE', @tanggal_awal,@tanggal_akhir
GO
drop table #temp
go
drop table #datatable
Update: To replace Null
DECLARE @COLS_ISNULL VARCHAR(MAX)='';
SELECT @COLS_ISNULL = @COLS_ISNULL +COLS_ISNULL+',' FROM (
SELECT distinct 'ISNULL('+QUOTENAME(convert(CHAR(10), datelist, 120))+',0) 'as COLS_ISNULL
from #temp)A
--to remove last comma.
SELECT @COLS_ISNULL = SUBSTRING (@COLS_ISNULL,1,LEN(@COLS_ISNULL)-1)
And replace the above @COLS_ISNULL in Select of your @query.
SET @query = 'SELECT product_id, '+@COLS_ISNULL+' FROM
(
select b.product_id, convert(CHAR(10), datelist, 120) PivotDate, product_ammount
from #datatable b
left join #temp d
on d.datelist between @tanggal_awal and @tanggal_akhir
and d.datelist = b.product_date
) x
pivot
(
sum(product_ammount)
for PivotDate in (' +@cols+ ')
) p'
From comments:
This is the Output from my SSMS
Upvotes: 2
Reputation: 121
based on Shakeer Mirza and Giorgos Betos Answer for this question, finally i have solved this problem. this is the full query for this problem
DECLARE @cols AS NVARCHAR(MAX)
DECLARE @colswithNoNulls AS NVARCHAR(MAX)
DECLARE @query AS NVARCHAR(MAX)
DECLARE @tanggal_awal date
DECLARE @tanggal_akhir date
DECLARE @print nvarchar(MAX)
CREATE TABLE #datatable
(
product_id int,
product_date date,
product_ammount int
)
SET @tanggal_awal = convert(date,'02-01-2017')
SET @tanggal_akhir = convert(date,'02-27-2017')
insert into #datatable (product_id,product_date,product_ammount) VALUES
(1,getdate(),100),
(1,getdate(),900),
(2,dateadd(DD,-1,getdate()),400),
(3,DATEADD(DD,4,getdate()),300),
(1,dateadd(DD,4,getdate()),200),
(2,dateadd(DD,2,getdate()),700),
(4,dateadd(DD,-3,getdate()),1000)
;WITH CTE (datelist,maxdate) AS
(
select min(@tanggal_awal) datelist, max(product_date) maxdate
from #datatable
union all
select dateadd(dd, 1, datelist), @tanggal_akhir
from cte
where datelist < maxdate
) SELECT c.datelist
into #temp
from cte c
select @cols = STUFF((SELECT distinct ',' + QUOTENAME(convert(CHAR(10), datelist, 120))
from #temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select @colswithNoNulls = STUFF((SELECT distinct ',ISNULL(' + QUOTENAME(convert(CHAR(10), datelist, 120)) +',''0'') '+ QUOTENAME(convert(CHAR(10), datelist, 120))
from #temp
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET @query =
'SELECT product_id, '+ @colswithNoNulls+' FROM
(
select b.product_id, coalesce(b.product_ammount,0) as product_ammount,
convert(CHAR(10), product_date, 120) PivotDate
from #datatable b
where product_date between @tanggal_awal and @tanggal_akhir
) x
pivot
(
sum(product_ammount)
for PivotDate in (' +@cols+ ')
) p'
EXECUTE sp_executesql @query ,N'@tanggal_awal DATE, @tanggal_akhir DATE', @tanggal_awal,@tanggal_akhir
GO
drop table #temp
go
drop table #datatable
the idea is from How to replace (null) values with 0 output in PIVOT. The idea itself is to make two different cols, one for only the cols and another one with ISNULL. Hopefully this problem can solve a lot of problem in the near future :)
Upvotes: 1
Reputation: 72165
Your query can be simplified to:
SET @query =
'SELECT product_id, '+@cols+' FROM
(
select b.product_id, b.product_ammount,
convert(CHAR(10), product_date, 120) PivotDate
from #datatable b
where product_date between @tanggal_awal and @tanggal_akhir
) x
pivot
(
sum(product_ammount)
for PivotDate in (' +@cols+ ')
) p'
You don't have to LEFT JOIN
to #temp
since for
clause in the pivot
operation is what really returns a list of all dates.
Upvotes: 1