SQL Query Dynamic PIVOT [duplicate] - sql

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
SQL Server dynamic PIVOT query?
I have temporary table with following structure:
MONTH ID CNT
----- ----------- ---
4 TOTAL_COUNT 214
5 TOTAL_COUNT 23
6 TOTAL_COUNT 23
4 FUNC_COUNT 47
5 FUNC_COUNT 5
6 FUNC_COUNT 5
4 INDIL_COUNT 167
5 INDIL_COUNT 18
6 INDIL_COUNT 18
How i can get the Pivot over month in this table like:
ID APRIL MAY JUNE
----------- ----- --- ----
TOTAL_COUNT 214 23 23
FUNC_COUNT 47 5 5
INDIL_COUNT 167 18 18
Please consider this table format. I am little messy in posting this format.

I'll leave the conversion of month integers to month names to you, but this will perform the PIVOT for you.
declare #t table
( [month] int, [id] nvarchar(20), [cnt] int )
insert #t values (4,'TOTAL_COUNT',214)
insert #t values (5,'TOTAL_COUNT',23)
insert #t values (6,'TOTAL_COUNT',23)
insert #t values (4,'FUNC_COUNT',47)
insert #t values (5,'FUNC_COUNT',5)
insert #t values (6,'FUNC_COUNT',5)
insert #t values (4,'INDIL_COUNT',167)
insert #t values (5,'INDIL_COUNT',18)
insert #t values (6,'INDIL_COUNT',18)
SELECT
[id], [4], [5], [6]
FROM
(SELECT [month], [id], [cnt] FROM #t) src
PIVOT
(SUM([cnt]) FOR [month] IN ([4], [5], [6])) p

While you can use a Static Pivot - one that you hard-code the months. In the comments, you stated that the number of months maybe be unknown, if that is the case then you will want to use a Dynamic Pivot to generate the list of months. Using a Dynamic Pivot gives you the flexibility of not knowing the columns you need until you run it.
create table t
(
[month] int,
[id] nvarchar(20),
[cnt] int
)
insert t values (4,'TOTAL_COUNT',214)
insert t values (5,'TOTAL_COUNT',23)
insert t values (6,'TOTAL_COUNT',23)
insert t values (4,'FUNC_COUNT',47)
insert t values (5,'FUNC_COUNT',5)
insert t values (6,'FUNC_COUNT',5)
insert t values (4,'INDIL_COUNT',167)
insert t values (5,'INDIL_COUNT',18)
insert t values (6,'INDIL_COUNT',18)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(month)
FROM t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, ' + #cols + ' from
(
select month, id, cnt
from t
) x
pivot
(
sum(cnt)
for month in (' + #cols + ')
) p '
execute(#query)
drop table t
The results would be:

Related

Combination of dynamic pivot and static pivot in SQL Server

Dynamic pivot combined combined with static aggregates
I have a table that looks something like this:
Place State Category CategCount MCount Buys Cost
London UK Old 3 NULL 22 4.50
London UK Old 6 5 3 22.00
Brussels BE Young 2 NULL 4 3.50
Brussels BE M NULL 5 12 1.20
Brussels BE M NULL 2 1 1.20
I basically need to:
Group by a number of fields (Place, State, Category in the example)
Count per such group
Sum MCount, Cost (and others, not in example) per group, these columns are static
Pivot over Category and sum CategCount for each such grouped category (here Old, Young). This is the dynamic part
Result should look like:
Count Place State Category SumMCount SumOld SumYoung SumCost SumBuys
2 London UK Old 5 9 0 26.50 25
1 Brussels BE Young 0 0 2 3.50 4
2 Brussels BE NULL 7 0 0 2.40 13
I know how to get a dynamic pivot query (as per https://stackoverflow.com/a/38505375/111575) and I know how to do the static part. But I don't know how to combine the two. Anybody any ideas? Maybe I go about it all wrong?
What I've got so far:
The following gives me the proper dynamic pivot results for Old and Young, but not sure how to add the count and the the 'regular' sums/aggregates to it:
create table #temp
(
Place nvarchar(20),
State nvarchar(20),
Category nvarchar(20) null,
CategCount int null,
MCount int null,
Buys int,
Cost int
)
insert into #temp values ('London', 'UK', 'Old', 3, NULL, 22, 4.50)
insert into #temp values ('London', 'UK', 'Old', 6, 5, 3, 22.00)
insert into #temp values ('Brussels', 'BE', 'Young', 2, NULL, 4, 3.50)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 5, 12, 1.20)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 2, 1, 1.20)
DECLARE #cols AS NVARCHAR(MAX)='';
DECLARE #query AS NVARCHAR(MAX)='';
SELECT #cols = #cols + QUOTENAME(Category) + ',' FROM (select distinct Category from #temp where CategCount IS NOT NULL) as tmp
select #cols = substring(#cols, 0, len(#cols)) --trim "," at end
--select (#cols) as bm
set #query =
'SELECT * from
(
select
sum(CategCount) as totalCatCount,
Category
from #temp
group by Category
) src
pivot
(
max(totalCatCount) for Category in (' + #cols + ')
) piv'
execute(#query)
drop table #temp
Returning:
And the following is the 'regular' query without the pivoting:
select count(*) as count, place, state, category,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(buys, 0)) as SumBuys,
sum(Cost) as SumCost
from #temp
group by place, state, category
Returning:
But it should look something like this:
I have used your static pivot part of the query as the source of dynamic pivot. Create two sets of dynamic pivot column list. One for pivoting and the another with Coalesce() to select pivoted columns (to convert null into 0). If there is no categcount for any category then that category has been replaced with null (case when). Two more aliases for Category and SumCatCount have been created since those were used in pivot condition.
Here goes your answer:
create table #temp
(
Place nvarchar(20),
State nvarchar(20),
Category nvarchar(20) null,
CategCount int null,
MCount int null,
Buys int,
Cost int
)
insert into #temp values ('London', 'UK', 'Old', 3, NULL, 22, 4.50)
insert into #temp values ('London', 'UK', 'Old', 6, 5, 3, 22.00)
insert into #temp values ('Brussels', 'BE', 'Young', 2, NULL, 4, 3.50)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 5, 12, 1.20)
insert into #temp values ('Brussels', 'BE', 'M', NULL, 2, 1, 1.20)
DECLARE #cols AS NVARCHAR(MAX)='';
DECLARE #query AS NVARCHAR(MAX)='';
DECLARE #colsForSelect AS NVARCHAR(MAX)='';
SET #cols = STUFF((SELECT distinct ',' + quotename(category)
FROM #temp where CategCount is not null
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #colsForSelect = STUFF((SELECT distinct ',' + ' Coalesce('+quotename(category)+',0) '+ quotename(category)
FROM #temp where CategCount is not null
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--select (#cols) as bm
set #query =
'SELECT count,place,state,(case when OldSumCatCount >0 then OldCategory else null end)Category,SumMCount, ' + #colsForSelect + ',SumCost,SumBuys from
(
select count(*) as count, place, state,category OldCategory, category,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(CategCount, 0)) as OldSumCatCount,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(Cost) as SumCost,
sum(ISNULL(buys, 0)) as SumBuys
from #temp
group by place , state, category
) src
pivot
(
max(SumCatCount) for Category in (' + #cols + ')
) piv
order by place desc,count'
execute(#query)
GO
count
place
state
Category
SumMCount
Old
Young
SumCost
SumBuys
2
London
UK
Old
5
9
0
26
25
1
Brussels
BE
Young
0
0
2
3
4
2
Brussels
BE
null
7
0
0
2
13
db<>fiddle here
Thanks to #Larnu in the comments for pointing me in the right direction. His/her statement on "you cannot JOIN a static to a dynamic query" and that either all or nothing has to be dynamic, prompted me to build onto the dynamic part and simply extend it.
I thought I needed to repeat the columns somehow in the PIVOT section, but that appears to not be the case. Only the column you want to pivot, apparently (logically so, once you think about it).
The only part I haven't figured out yet is how to get rid of NULL in the resulting set, hopefully someone answers with that in mind ;).
DECLARE #cols AS NVARCHAR(MAX)='';
DECLARE #query AS NVARCHAR(MAX)='';
SELECT #cols = #cols + QUOTENAME(Category) + ',' FROM (select distinct Category from #temp where CategCount IS NOT NULL) as tmp
select #cols = substring(#cols, 0, len(#cols)) --trim "," at end
--select (#cols) as bm
set #query =
'SELECT * from
(
select
count(*) as count,
Place,
State,
Category,
Category as CatPivot,
sum(ISNULL(CategCount, 0)) as TotalCatCount,
sum(ISNULL(Buys, 0)) as SumBuys,
sum(ISNULL(Cost, 0)) as SumCost,
sum(ISNULL(MCount, 0)) as SumMCount
from #temp
group by Category, Place, State
) src
pivot
(
max(TotalCatCount) for CatPivot in (' + #cols + ')
) piv'
execute(#query)
Here I am sharing another answer which is same but as suggested by #Anthony Hancock dynamic column names for pivot have been created with string_agg() instead of stuff() and xml path for(). It's way too faster and more readable (for SQL Server 2017 and onward)
DECLARE #cols AS NVARCHAR(MAX)='';
DECLARE #query AS NVARCHAR(MAX)='';
DECLARE #colsForSelect AS NVARCHAR(MAX)='';
select #cols =string_agg(category,',') from (
select distinct category FROM #temp where CategCount is not null )t
select #colsForSelect= STRING_AGG(category,',') from
(select distinct 'coalesce('+category+',0) '+category category FROM #temp where CategCount is not null )t
set #query =
'SELECT count,place,state,(case when OldSumCatCount >0 then OldCategory else null end)Category,SumMCount, ' + #colsForSelect + ',SumCost,SumBuys from
(
select count(*) as count, place, state,category OldCategory, category,
sum(ISNULL(MCount, 0)) as SumMCount,
sum(ISNULL(CategCount, 0)) as OldSumCatCount,
sum(ISNULL(CategCount, 0)) as SumCatCount,
sum(Cost) as SumCost,
sum(ISNULL(buys, 0)) as SumBuys
from #temp
group by place , state, category
) src
pivot
(
max(SumCatCount) for Category in (' + #cols + ')
) piv
order by place desc,count'
execute(#query)

Dynamic Pivot Table with date column

I have a table in which information about a stock for an items are available.
This is how the table looks like:
ITEMCODE DATE INSTOCK
-----------------------------
ABC001 2019-01-04 10
ABC001 2019-02-04 10
ABC001 2019-03-04 10
ABC001 2019-04-04 5
ABC001 2019-05-04 5
Is it possible to get output like this:
Itemcode 01/04/2019 02/04/2019 03/04/2019 04/04/2019 05/04/2019
-------------------------------------------------------------------------
ABC001 10 10 10 5 5
This is the query which i have used...
SELECT T0.ITEMCODE,T0.INSTOCK
FROM DBO.TABLE_2 T0
WHERE T0.DATE >='[%0]'AND T0.DATE <= '[%1]'
But after doing some research, I have found out its possible using pivot table...
How to modify my query in order to get the desired output
I was able to get it with the code below, you just need to replace #table with your table name. Also ignore the first part of the code that sets up the table.
There are similar questions/answers here: SQL Server dynamic PIVOT query?
-------------------------------------------------------------------
IF OBJECT_ID('tempdb..#table') IS NOT NULL
BEGIN
DROP TABLE #table
END
CREATE TABLE #table(ITEMCODE VARCHAR(10),DATE date,INSTOCK int)
insert into #table values('ABC001','2019-01-04',10)
insert into #table values('ABC001','2019-02-04',10)
insert into #table values('ABC001','2019-03-04',10)
insert into #table values('ABC001','2019-04-04',5)
insert into #table values('ABC001','2019-05-04',5)
-------------------------------------------------------------------
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.DATE)
FROM #table c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ITEMCODE, ' + #cols + ' from
(
select ITEMCODE
, DATE
, INSTOCK
from #table
) x
pivot
(
sum(INSTOCK)
for DATE in (' + #cols + ')
) p '
execute(#query)

creating dynamic pivot using sql [duplicate]

This question already has answers here:
SQL Server dynamic PIVOT query?
(9 answers)
Closed 7 years ago.
i don't want to use static pivot because my values are dynamic, here is what my table look like
ItemName Date Qty
Tomato 11/29/2015 20
Tomato 11/30/2015 15
Apple 12/2/2015 41
Mango 12/3/2015 23
Onion 12/4/2015 11
but i want to look like this but i can't hard code the ItemName and the Date because they are always dynamic.
Try like Below Query
--Sample Table Creation
create table veg
( itemname varchar(50),
[date] date,
qty int
)
insert into veg
values
('Tomato', '11/29/2015', 20),
('Tomato', '11/30/2015' , 15),
('Apple' , '12/2/2015' ,41),
('Mango' , '12/3/2015' , 23),
('Onion' , '12/4/2015' , 11)
--dynamic sql
DECLARE #col AS NVARCHAR(MAX)
DECLARE #val AS NVARCHAR(MAX)
SET #col = STUFF((SELECT distinct ',' + QUOTENAME(c.date)
FROM veg c
FOR XML PATH('')
),1,1,'')
--select #col
set #val = 'SELECT itemname, ' + #col + ' from
(
SELECT itemname,date,qty
FROM veg
) SOURCE
PIVOT
(
SUM(qty)
FOR date IN (' + #col + ')
) p '
exec (#val)

Constructing a PIVOT

I think that PIVOT will help me accomplish this, but I can't get anything started. I am having serious SQL brain farts today, I need some help.
Here is the output I have now:
Id Name Question Answer
0 Test Vault A
0 Test Container 1
1 Foo Vault B
1 Foo Container 2
And this is my desired output:
Id Name Vault Container
0 Test A 1
1 Foo B 2
Can this be done?
If that is impossible or terribly complex to do, I have an alternate way to approach this. The output for my alternate query is:
Id Name VaultId ContainerId
0 Test A NULL
0 Test NULL 1
1 Foo B NULL
1 Foo NULL 2
And here I need to be able to suppress it into one row per Id/Name. I can't remember how to do either of these!
DECLARE #Test TABLE
(
Id INT
,[Name]VARCHAR(10) NOT NULL
,Question VARCHAR(10) NOT NULL,
Answer VARCHAR(10)
);
INSERT #Test VALUES (0,'test1', 'vault','a');
INSERT #Test VALUES (0,'test1', 'Container ','1');
INSERT #Test VALUES (1,'test4', 'vault','b');
INSERT #Test VALUES (1,'test4', 'Container','2');
;WITH CTE
AS
(
SELECT t.id, t.[Name], t.[Question ] ,t.Answer
FROM #Test t
)
SELECT *
FROM CTE
PIVOT ( max(answer) FOR Question IN (vault,container) ) f;
You could do this with a Static Pivot:
create table temp
(
id int,
name varchar(10),
question varchar(10),
answer varchar(10)
)
INSERT into temp VALUES (0,'test', 'vault','a');
INSERT into temp VALUES (0,'test', 'Container','1');
INSERT into temp VALUES (1,'foo', 'vault','b');
INSERT into temp VALUES (1,'foo', 'Container','2');
select *
from
(
select id, name, question, answer
from temp
) x
pivot
(
max(answer)
for question in ([container], [vault])
) p
drop table temp
or a dynamic pivot
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.question)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, name, ' + #cols + ' from
(
select id, name, question, answer
from temp
) x
pivot
(
max(answer)
for question in (' + #cols + ')
) p '
execute(#query)
both will give you the same results:
Yeah PIVOT is what you need here :). Assuming your table is called MyPivot Try:
SELECT Id, Name, [Vault], [Container]
FROM (SELECT Id, Name, Question, Answer FROM MyPivot) AS SourceTable
PIVOT (MAX(Answer) FOR Question in (Vault, Container)) as p;
EDIT: To demonstrate what that syntax means, see the following breakdown:
PIVOT (<aggregate function>(<column being aggregated>)
FOR <column that contains the values that will become column headers>
IN ( [first pivoted column], [second pivoted column])

sql query to show results vertically

How to transform this:
ID Name Description
1 Test1a TestDesc1a
1 Test1b TestDesc1b
2 Test2a TestDesc2a
2 Test2b TestDesc2b
into this:
ID Column 1 2
1 Name test1a test1b
1 Description testDesc1a testDesc1b
You ask for complex pivoting table. Read about this here: http://msdn.microsoft.com/en-us/library/ms177410.aspx
You need to use a PIVOT query.
A basic discussion of PIVOT queries can be found at [MSDN][1].
This is a tricky question to solve, however the output specified is not proper/ conflicting. Below is similar solution which you can try
Sample Table creation:
CREATE TABLE [dbo].[TestTable](
[Id] [int] NULL,
[Name] [nvarchar](50) NULL,
[Description] [nvarchar](50) NULL)
Inserting sample values:
INSERT INTO TestTable VALUES (1,'Test1a','TestDesc1a')
INSERT INTO TestTable VALUES (2,'Test1b','TestDesc1b')
INSERT INTO TestTable VALUES (3,'Test2a','TestDesc2a')
INSERT INTO TestTable VALUES (4,'Test2b','TestDesc2b')
Query to get the desired output using Pivot:
SELECT 'Name' AS [Column], [1], [2],[3],[4]
FROM
(SELECT Name, id from TestTable) AS ST
PIVOT
(Max(Name) FOR ID IN ([1], [2],[3],[4])) AS PT
UNION
SELECT 'Description' AS [Column], [1], [2],[3],[4]
FROM
(SELECT id,[Description] from TestTable) AS ST
PIVOT
(Max([Description]) FOR ID IN ([1], [2],[3],[4])) AS PT
ORDER BY [Column] DESC
OutPut:
Column 1 2 3 4
Name Test1a Test1b Test2a Test2b
Description TestDesc1a TestDesc1b TestDesc2a TestDesc2b
Hope this helps to solve your question.
You can use a static PIVOT if you only are going to have a few columns but I am guessing that you will have more than 2 IDs so you will probably want to use a Dynamic PIVOT for this query. Using a dynamic pivot will allow you to have more than the two ids you provided, it will grab all of the Ids from the table and then PIVOT:
create table temp
(
id int,
name varchar(10),
description varchar(20)
)
insert into temp values (1, 'Test1a', 'TestDesc1a')
insert into temp values (1, 'Test1b', 'TestDesc1b')
insert into temp values (2, 'Test2a', 'TestDesc2a')
insert into temp values (2, 'Test2b', 'TestDesc2b')
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.id)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT ''Name'' as [Column], ' + #cols + ' from
(
select id
, name
from temp
) x
pivot
(
max(name)
for id in (' + #cols + ')
) p
UNION
SELECT ''Description'' as [Column], ' + #cols + ' from
(
select id
, description
from temp
) x
pivot
(
max(description)
for id in (' + #cols + ')
) p '
execute(#query)
drop table temp
Results:
Column 1 2
Description TestDesc1b TestDesc2b
Name Test1b Test2b