how to make a dynamic sql pivot - sql

I have a select statement with the following
select country,city,school,class,quantity from tableA
I want the pivot to be based on class like in the below table:
country City School ClassA ClassB ClassC ClassD
XXX AAA SCH01 37 37 39 37
XXX BBB SCH02 12 12 1 12
XXX BBB SCH03 6 6 9 6
XXX DDD SCH04 1 1 1 1
YYY ABC SCH05 1 1 1 1
YYY CDE SCH06 1 1 1 1
YYY EDY SCH07 1 1 1 1
YYY ZER SCH08 1 1 1 1
SSS GFY SCH09 1 1 1 1
SSS AHY SCH10 1 1 1 1

MS SQL SERVER:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME([class])
from [table-name]
group by [class]
order by [class]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [country], [city], [school],' + #cols + ' from
(
select [country], [city], [school],[class],[quantity]
from [table-name]
) x
pivot
(
sum([quantity])
for [class] in (' + #cols + ')
) p '
execute(#query);

Related

how to remove null values from an sql pivot?

How can i replace the null values with zero in the sql pivot table?
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.[Offer_cover])
FROM #cover2 c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Ref,role_name,offer_id, ' + #cols + ' from
(
select*
from #cover2
) x
pivot
(
SUM(cover_earning_Count)
for [offer_cover] in (' + #cols + ')
) p '
execute(#query)
Output:
Ref role_name offer_id 10000 104000 112000
43132_43282 Call Center 1 1 NULL NULL
43132_43282 Others 1 2 NULL NULL
43160_43282 Call Center 1 6 NULL 1
43160_43282 Others 1 NULL 1 NULL
43191_43282 Call Center 1 7 NULL NULL
43191_43282 Others 1 3 1 1
43221_43282 Call Center 1 4 1 1
43221_43282 Others 1 2 NULL NULL
Cover2 Table
Ref YEAR MONTH Role_name offer_cover offer_id Cover_Earning_Count CONF_DATE Curr_date
43132_43282 2018 2 Call Center 2000 1 5 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 4000 1 8 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 6000 1 2 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 8000 1 4 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 10000 1 1 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 12000 1 6 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 14000 1 2 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 16000 1 4 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 20000 1 6 2/1/2018 7/1/2018
43132_43282 2018 2 Call Center 24000 1 5 2/1/2018 7/1/2018
as i have mention earlier in my comments, you need 2 #cols, one for the select and another for the pivot
DECLARE #cols AS NVARCHAR(MAX), -- for pivot
#cols2 AS NVARCHAR(MAX), -- for select
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.[Offer_cover])
FROM #cover2 c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
-- this is for the SELECT
SET #cols2 = STUFF((SELECT distinct ',' + 'ISNULL(' + QUOTENAME(c.[Offer_cover]) + ', 0) ' + QUOTENAME(c.[Offer_cover])
FROM #cover2 c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Ref,role_name,offer_id, ' + #cols2 + ' from
(
select *
from #cover2
) x
pivot
(
SUM(cover_earning_Count)
for [Offer_cover] in (' + #cols + ')
) p'
-- do a print to verify the query
print #query
Try using ISNULL
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.[Offer_cover])
FROM #Table1 c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Ref,role_name,offer_id, ' + isnull(#cols,0) + ' from
(
select*
from #Table1
) x
pivot
(
SUM(cover_earning_Count)
for [offer_cover] in (' + #cols + ')
) p '
execute(#query)

How to split group result into unlimited individual columns

If I have a table:
id tstamp x y
-----------------
1 111 1 A
2 111 2 B
3 111 3 C
4 222 1 D
5 222 2 E
6 222 3 F
7 333 1 G
8 333 2 H
9 333 3 I
... nnn ... ...
How can I split it into the following, where the number of columns is dependent on a range of tstamp?
x y111 y222 y333 ynnn
----------------------------------
1 A D G ...
2 B E H ...
3 C F I ...
It seems fundamentally simple, but I can't find any relevant example or API? There's a good 10 or so similar questions but they all deal with string handling, csv, xml???
I imagine it would be pretty straight forward to do in c# or another scripting language, but I would expect it should be an easy thing in sql?
you need to construct column values of tstamp for PIVOT and use dynamic query
DECLARE #cols NVARCHAR(MAX)
SELECT #cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT
'],[' + tstamp
FROM Table1
ORDER BY '],[' + tstamp
FOR XML PATH('')
), 1, 2, '') + ']'
DECLARE #query NVARCHAR(4000)
SET #query = N'SELECT x, '+
#cols +'
FROM
(SELECT x, y, tstamp
FROM Table1
) p
PIVOT
(
MAX(y)
FOR tstamp IN
( '+
#cols +' )
) AS pvt
'
exec sp_executesql #query

Aggregate function within inner select in Pivot query using SQL Server

I have the following table:
select * from product;
slno item
---------------
1 HDD
2 PenDrive
3 RAM
4 DVD
5 RAM
6 HDD
7 RAM
7 RAM
7 RAM
Now I need to do pivoting for this table for which i am using following query:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(item)
from product
group by item
order by item
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT slno,TotalProduct ,' + #cols + '
from
(
select slno,Count(*) as TotalProduct,item
from product
group by slno,item
) x
pivot
(
count(item)
for item in (' + #cols + ')
) p '
exec(#query)
Result:
slno TotalProducts DVD HDD PenDrive RAM
---------------------------------------------
1 1 0 1 0 0
2 1 0 0 1 0
3 1 0 0 0 1
4 1 1 0 0 0
5 1 0 0 0 1
6 1 0 1 0 0
7 3 0 0 0 1
Note The total of product RAM is 3 but in Column RAM showing only 1. I have used COUNT(*) aggregate function within the inner select statement in #query. How can i show actual count?
You only need to group by slno, not by the combination of slno and item. Therefore, you need to change the query which provides a source for your pivot as follows:
set #query = 'SELECT slno,totalproduct,' + #cols + '
from
(
select p.slno slno, c.count as totalproduct, p.item
from product p
inner join
(select slno, count(item) count
from product
group by slno) c on p.slno = c.slno
) x
pivot
(
count(item)
for item in (' + #cols + ')
) p '
Demo
Use following sub query instead of your sub query:
select slno,Count(*) OVER (PARTITION BY slno) as TotalProduct,item
from product
Edit: Count(*) Over(Partition by ...) supported in SQL Server 2012 and above versions.

How can I return several text fields with the same ID number in one row in SQL server?

I have table:
ID Note
1 1 aaa
2 1 bbb
3 1 ccc
4 2 ddd
5 2 eee
6 2 fff
I need to return it as:
ID Note1 Note2 Note3
1 1 aaa bbb ccc
2 2 ddd eee fff
Thank you!
You can use the PIVOT function for this type of query. If you have a known number of columns, then you can hard-code the values:
select *
from
(
select id, note,
'Note' +
cast(row_number() over(partition by id order by id) as varchar(10)) col
from yourtable
) x
pivot
(
max(note)
for col in ([Note1], [Note2], [Note3])
) p
See SQL Fiddle with Demo
If you are going to have an unknown number of notes that you want to turn into columns, then you can use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ','
+ QUOTENAME('Note' +
cast(row_number() over(partition by id order by id) as varchar(10)))
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id,' + #cols + ' from
(
select id, note,
''Note'' +
cast(row_number() over(partition by id order by id) as varchar(10)) col
from yourtable
) x
pivot
(
max(note)
for col in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
Both will produce the same results.
| ID | NOTE1 | NOTE2 | NOTE3 |
------------------------------
| 1 | aaa | bbb | ccc |
| 2 | ddd | eee | fff |
Or if you do not want to use the PIVOT function, then you can use an aggregate function with a CASE statement:
select id,
max(case when rn = 1 then note else '' end) Note1,
max(case when rn = 2 then note else '' end) Note2,
max(case when rn = 3 then note else '' end) Note3
from
(
select id, note,
row_number() over(partition by id order by id) rn
from yourtable
) src
group by id
See SQL Fiddle with Demo

Pivot with dynamic columns

There are 4 tables:
Suppl, fields: (Code_name, Code_name_arch, Tasknum, Pki_num, Group_eng, Name, Descr, Cost, Quan, shop);
Maker, fields : (Code_maker, Code_maker_arch, Code_name, provider);
Arrival, fields: (Code_arr, Code_maker, quan_arr);
Accounts, fields: (Code_acc, Code_maker, num_acc, quan_acc, summ)
my query is:
ALTER Procedure [dbo].[pr_tblz] As
Set NOCOUNT ON
Declare #task VARCHAR(4000)
Select #task = coalesce(#task + ',[' + [Tasknum] + ']',
'[' + [Tasknum] + ']')
FROM [db_pki].[dbo].[Suppl]
Group BY [Tasknum]
Order BY [Tasknum]
Declare #query VARCHAR(8000)
Set #query='
Alter View Amountzz As
SELECT Shop, Name, Desc, Group_eng as GI, '+ #task +', quan_arr As specif, quan_acc As acns
FROM
(
select
Shop, Name, Desc, Group_eng, Tasknum, Quan, quan_arr, quan_acc
from [db_pki].[dbo].[Suppl] as deliveries
LEFT JOIN [db_pki].[dbo].[Maker] ON (deliveries.[Code_name] = [db_pki].[dbo].[Maker].[Code_name])
LEFT JOIN [db_pki].[dbo].[Arrival] ON ([db_pki].[dbo].[Maker].[Code_maker] = [db_pki].[dbo].[Arrival].[Code_maker])
LEFT JOIN [db_pki].[dbo].[Accounts] ON ([db_pki].[dbo].[Maker].[Code_maker] = [db_pki].[dbo].[Accounts].[Code_maker])
)date_to_pivot
PIVOT
(
Max([Quan])
For [Tasknum]
IN (' + #task + ')
)AS p'
Execute (#query)
result:
Shop Name Descr GI n1 n2 n3 ... specif acns
1 name1 1 5 4 1 1
2 10 name2 2 3 8 2 2
3 name3 3 501 11 3 3
1 8 name1 1 5 16 7 10
a 2 10 name2 2 3 3 5 6
5 name1 1 2 5 6 3
How can I get the following result?
Shop Name Descr GI n1 n2 n3 ... specif acns
1 8 name1 1 5 4 16 8(1+7) 11
a 2 10 name2 2 3 8 3 7 8(2+6)
3 name3 3 501 11 3 3
5 name1 1 2 5 6 3
n1, n2, n3,... - Tasknum
Not tested. Try aggregating the values of Shop, specif and acns in the subselect that pulls the columns before the pivoting, like this (the necessary changes are highlighted in bold):
ALTER Procedure [dbo].[pr_tblz] As
Set NOCOUNT ON
Declare #task VARCHAR(4000)
Select #task = coalesce(#task + ',[' + [Tasknum] + ']',
'[' + [Tasknum] + ']')
FROM [db_pki].[dbo].[Suppl]
Group BY [Tasknum]
Order BY [Tasknum]
Declare #query VARCHAR(8000)
Set #query='
Alter View Amountzz As
SELECT Shop, Name, Desc, Group_eng as GI, '+ #task +', quan_arr As specif, quan_acc As acns
FROM
(
select
MAX(Shop ) OVER (PARTITION BY Name) AS Shop,
Name, Desc, Group_eng, Tasknum, Quan,
SUM(quan_arr) OVER (PARTITION BY Name) AS quan_arr,
SUM(quan_acc) OVER (PARTITION BY Name) AS quan_acc
from [db_pki].[dbo].[Suppl] as deliveries
LEFT JOIN [db_pki].[dbo].[Maker] ON (deliveries.[Code_name] = [db_pki].[dbo].[Maker].[Code_name])
LEFT JOIN [db_pki].[dbo].[Arrival] ON ([db_pki].[dbo].[Maker].[Code_maker] = [db_pki].[dbo].[Arrival].[Code_maker])
LEFT JOIN [db_pki].[dbo].[Accounts] ON ([db_pki].[dbo].[Maker].[Code_maker] = [db_pki].[dbo].[Accounts].[Code_maker])
)date_to_pivot
PIVOT
(
Max([Quan])
For [Tasknum]
IN (' + #task + ')
)AS p'
Execute (#query)