SQL pivot with multiple column data in one with comma separation - sql

SQL Query Table:
Date Production LineName Buyer Style
2021-04-30 100 A Lev 1a
2021-04-30 200 B Kont 2d
2021-04-30 300 C ABB 4l
2021-04-30 400 A Lev 1a
2021-04-29 600 A BDA 3z
Output need like:
LineName 2021-04-29 2021-04-30
A 600,BDA,3Z 500,Lev,1a
B NULL 200,Kont,2d
C NULL 300,ABB,4l
I started coding and stuck at
DECLARE #query VARCHAR(4000)
,#Dates VARCHAR(4000)
SET #Dates = STUFF((
SELECT DISTINCT '],[' + Cast([Date] As Varchar)
FROM Table_17M
ORDER BY '],[' + Cast([Date] As Varchar)
FOR XML PATH('')
), 1, 2, '') + ']'
SET #query = ';With PivotData
As
(
Select
LineName, -- grouping element
[Date], -- spreading element
PlanProduction-- aggregating element
From Table_17M
)
Select *
From PivotData
Pivot (Sum(PlanProduction) For [Date] In (' + #Dates + ')) As P;'
EXEC (#query);
Any idea please share. It will very much helpful. Thanks.

If you are using SQLServer 2017 or later version then use string_agg() instead of suff(). It's easier to implement and much faster.
select #cols= string_agg(quotename([date]),',') from (select distinct [date] from Table_17M)t
DB-Fiddle
create table Table_17M(Date Date,Production int, LineName varchar(50), Buyer varchar(50), Style varchar(50));
insert into Table_17M values('2021-04-30', 100, 'A', 'Lev', '1a');
insert into Table_17M values('2021-04-30', 200, 'B', 'Kont', '2d');
insert into Table_17M values('2021-04-30', 300, 'C', 'ABB' , '4l');
insert into Table_17M values('2021-04-30', 400, 'A', 'Lev' , '1a');
insert into Table_17M values('2021-04-29', 600, 'A', 'BDA' , '3z');
Query:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
SET #cols = STUFF((SELECT distinct ',' + quotename([date])
FROM Table_17M
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'select LineName , ' + #cols + ' from
(
select LineName,[date],concat(sum(production),'','',max(buyer),'','',string_agg(Style,'','')) other_columns
from Table_17M
group by LineName,[date]
) x
pivot
(
max(other_columns)
for [date] in (' + #cols + ')
) p
group by LineName , ' + #cols
execute( #query)
Output:
LineName
2021-04-29
2021-04-30
A
600,BDA,3z
500,Lev,1a,1a
B
null
200,Kont,2d
C
null
300,ABB,4l
db<fiddle here

Related

How to create a SQL Server Pivot query?

I want to create sql query result. I want to dynamically create because of inventory locations are one or more. (Example.. CTE)
Item_id Location Qty
--------------------------
1 a 1
2 b 2
3 c 3
3 a 1
2 c 2
1 b 3
Result is....
Item_id a_Location_Qty b_Location_Qty c_Location_Qty
-------------------------------------------------------
1 1 3 0
2 0 2 2
3 1 0 3
Please try below code
Create Table #Stack10072017040904(Item_id varchar(100),Location varchar(100),Qty int)
insert into #Stack10072017040904 select '1','a','1'
insert into #Stack10072017040904 select '2','b','2'
insert into #Stack10072017040904 select '3','c','3'
insert into #Stack10072017040904 select '3','a','1'
insert into #Stack10072017040904 select '2','c','2'
insert into #Stack10072017040904 select '1','b','3'
select Item_id,Isnull(a,0) a_Location_Qty
,isnull(b,0)b_Location_Qty
,isnull(c,0)c_Location_Qty
from #Stack10072017040904
pivot
(Sum(Qty) for Location in ([a],[b],c) )pvt
drop table #Stack10072017040904
Try this Dynamic Sql
Declare
#Sql nvarchar(max),
#dynamicCol nvarchar(max),
#dynamicCol2 nvarchar(max)
--Create columns Dynamically
SELECT #dynamicCol=STUFF((SELECT DISTINCT ', '+ QUOTENAME(Location )
From #Stack10072017040904 For XML PATH ('')),1,1,'')
SELECT #dynamicCol2=STUFF((SELECT DISTINCT ', '+ 'ISNULL ('+ QUOTENAME(Location )+ ','+'''0'''+')' +' AS '+Location+'_Location_Qty'
From #Stack10072017040904 For XML PATH ('')),1,1,'')
SET #Sql='
SELECT [Item_id] ,'+ #dynamicCol2 +' From
(
SELECT * From
#Stack10072017040904
)AS Src
PIVOT
(
MAX([Qty]) For [Location] IN ('+#dynamicCol+')
)
AS Pvt'
PRINT #Sql
EXEC(#Sql)
Result
Item_id a_Location_Qty b_Location_Qty c_Location_Qty
-------------------------------------------------------
1 1 3 0
2 0 2 2
3 1 0 3
Build columns dynamically and use a dynamic PIVOT:
Create Table #Test(Item_id varchar(100),Location varchar(100),Qty int)
insert into #Test values( '1','a','1')
insert into #Test values( '2','b','2')
insert into #Test values( '3','c','3')
insert into #Test values( '3','a','1')
insert into #Test values( '2','c','2')
insert into #Test values( '1','b','3')
DECLARE #cols1 AS NVARCHAR(MAX), #cols2 AS NVARCHAR(MAX), #query AS NVARCHAR(MAX);
SET #cols1 = STUFF((SELECT distinct ', isnull(' + quotename(s.Location) + ', 0) as ' + quotename(s.Location)
FROM #Test s
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #cols2 = STUFF((SELECT distinct ',' + quotename(s.Location)
FROM #Test s
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT Item_id, ' + #cols1 + ' from
(
select Item_id
, Location
, Qty
from #Test
) x
pivot
(
max(qty)
for Location in (' + #cols2 + ')
) p '
execute(#query)
drop table #Test

How to pivot rows into colums dynamically SQL Server

I have a request which returns something like this:
--------------------------
Tool | Week | Value
--------------------------
Test | 20 | 3
Sense | 20 | 2
Test | 19 | 2
And I want my input to look like this:
-------------------------
Tool | W20 | W19
-------------------------
Test | 3 | 2
Sense | 2 | null
Basically, for every week I need to have a new column. The number of week and of tools is dynamic.
I have tried many things but nothing worked. Anybody have a solution ?
Try this
CREATE table #tst (
Tool varchar(50), [Week] int, Value int
)
insert #tst
values
('Test', 20, 3),
('Sense', 20,2),
('Test', 19, 2)
Here is the Dynamic Query:
DECLARE #col nvarchar(max), #query NVARCHAR(MAX)
SELECT #col = STUFF((SELECT DISTINCT ',' + QUOTENAME('W' + CAST([Week] as VARCHAR))
from #tst
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = '
SELECT *
FROM (
SELECT Tool,
Value,
''W'' + CAST([Week] as VARCHAR) AS WeekNo
FROM #tst
) t
PIVOT
(
MAX(t.Value)
FOR WeekNo IN (' + #col + ')
) pv
ORDER by Tool'
EXEC (#query)
Result
Tool W20 W19
=================
Sense 2 NULL
Test 3 2
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
( Tool varchar(5), Week int, Value int)
;
INSERT INTO #temp
( Tool , Week , Value )
VALUES
('Test', 20, 3),
('Sense', 20, 2),
('Test', 19, 2)
;
DECLARE #statement NVARCHAR(max)
,#columns NVARCHAR(max),
#col NVARCHAR(max)
SELECT #columns = ISNULL(#columns + ', ', '') + N'[' +'w'+ tbl.[Week] + ']'
FROM (
SELECT DISTINCT CAST([Week] AS VARCHAR)[Week]
FROM #temp
) AS tbl
SELECT #statement = 'SELECT *
FROM
(
SELECT
Tool , ''w''+ CAST(Week AS VARCHAR) week , Value
FROM
#Temp
) src
PIVOT(MAX(Value)for Week in (' + #columns + ')) as pvt
'
EXEC sp_executesql #statement = #statement
This is how I would do it ... If I understood your question correctly
if object_id('tempdb..#InputTool') is not null drop table #InputTool
create table #InputTool (Tool nvarchar(10), [20] int, [19] int)
insert into #InputTool (Tool, [20], [19])
values
('Test', 3, 2),
('Sense', 2, null)
declare #cols nvarchar(max)
select #cols = STUFF((SELECT ',' + QUOTENAME(name)
from tempdb.sys.columns
where object_id = object_id('tempdb..#InputTool')
and Column_id > 1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
declare #sqlquery nvarchar(max) =
'select Tool, Weeks, Value from (
select * from #InputTool
) as it
UNPIVOT
(
Value FOR Weeks IN (' + #cols + ')
) AS Weeks
order by Weeks desc'
execute (#sqlquery);
Give it a shot and let me know if it worked

Insert query results into temp table

I've recently learned from here how to do PIVOT in SQL, and I actually took an example from the other question on here. It works perfectly.
However, I want to perform additional joins, after the query, but I am unable to insert into temporary table the results of query? How may I do that?
Create table
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);
Perform pivoting query
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)
The result is
store 1 2 3
101 138 282 220
102 96 212 123
105 37 78 60
109 59 97 87
But Id like to have it in #temp table, and I tried placing INTO #temp before 'Execute Query' and before FROM statement within Query.
Any idea? I am aware of SELECT * INTO #temp FROM BlaBla but seems its diff with Queries.
You can create a global temp table dynamically
declare #tblName nvarchar(10)
set #tblName = N'##Temp' + cast(##spid as nvarchar(5))
declare #tblCreate nvarchar(max)
SET #tblCreate = N'create table ' + #tblName + ' ('
+ REPLACE(#cols,',',' int,') + N' int)'
EXECUTE sp_executesql #tblCreate
And then edit your #query to insert into the table.
set #query = 'INSERT INTO ' + #tblName + ' SELECT store,' + #cols + ' from
(
select store, week, xCount
from yt
) x
pivot
(
sum(xCount)
for week in (' + #cols + ')
) p;
drop table ' + #tblName
execute(#query)
The problem you're facing with select ... into #temp inside the execute is that the table gets created, but due to being in separate scope, it gets dropped immediately when the execute ends, so your procedure can't see it.
Your code will work, if you create the table before calling execute and just use insert into. You can check this in SQL Fiddle. This just causes the problem that if your query is dynamic, how to create the table so that it fits the query.
Trying to create logic where you dynamically adjust the number of columns in SQL is not simple to do, and really isn't something you should be doing. Better way would be to handle that in your presentation layer.
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 + ' Into ##tempyt from
(
select store, week, xCount
from yt
) x
pivot
(
sum(xCount)
for week in (' + #cols + ')
) p '
DROP TABLE IF EXISTS ##tempyt;
execute(#query);
select * from TempDB.sys.##tempyt

SQL query Joining table itself

I have a following table:
TICKER DATE SHAREPRICE
--------------------------------
ABC 1.1.2015 50
XYZ 1.1.2015 100
etc.
I want to make a query, where the result is following:
DATE PRICE(TICKER ABC) PRICE(TICKER XYZ)
--------------------------------------------
1.1.2015 50 100
Use PIVOT in SQL SERVER.
DECLARE #test AS TABLE(TICKER VARCHAR(10), DATE DATETIME, SharePrice INT)
INSERT INTO #test
SELECT 'ABC', '1/1/2015', 50 UNION
SELECT 'XYZ', '1/1/2015', 100
SELECT Date, ABC AS [PRICE(TICKER ABC)], XYZ AS [PRICE(TICKER XYZ)] FROM #test
PIVOT(MAX(SharePrice) FOR Ticker IN(ABC, XYZ)) AS A
In TSQL you can write a query using dynamic pivot as:
DECLARE #cols AS NVARCHAR(MAX),#colsFinal AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ', ' + TICKER
from test1
group by TICKER, [DATE]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsFinal = STUFF((SELECT distinct ', isnull( ' + TICKER +
' ,0) as [PRICE (TICKER ' + TICKER +' )]'
from test1
group by TICKER, [DATE]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT [Date],' + #colsFinal + N' from
(
select [Date], TICKER , SharePrice
from test1
) x
pivot
(
max(SharePrice)
for TICKER in (' + #cols + N')
) p '
exec sp_executesql #query;
DEMO

Convert vertical sql result into horizontal output with Group by fields in MS SQL Server

I have a table( in SQL Server 2008) which has vertical sales transactions data, i would like to convert vertical output to horizontal output, i tried to use PIVOT, but some how not getting idea how to apply group by on PIVOT, as i want Sum based on AccountHeadID, TransType and IsPast Column.
Sample Table
CREATE TABLE Trans
([TransID] int,
[CustID] int,
[AccountHeadID] int,
[TransType] varchar(100),
[IsPast] bit,
[Amount] int)
;
INSERT INTO Trans
([TransID],CustID, [AccountHeadID], [TransType], [IsPast],[Amount])
VALUES
(1, 1, 1, 'Sales',1,1000),
(2, 1, 1, 'Sales',0,500),
(3, 1, 1, 'Sales',0,400),
(4, 1, 1, 'Return',0,300),
(5, 1, 1, 'Return',0,200),
(6, 1, 1, 'Return',0,100),
(7, 1, 1, 'Return',1,150),
(8, 1, 2, 'Sales',1,2000)
Current Query
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(AccountHeadID)
from Trans
group by AccountHeadID
order by AccountHeadID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT CustID,' + #cols + '
from
(
SELECT
a.CustID,
a.AccountHeadID,
a.TransType,
a.Amount,
a.isPast
FROM Trans a
) x
pivot
(
sum(Amount)
for AccountHeadID in (' + #cols + ')
) p '
execute sp_executesql #query;
Expected Output
CustID | Account-HeadID-TransType-IsPast[1-Sales-Past] | Account-HeadID-TransType-IsCurrent[1-Sales-Current] | Account-HeadID-TransType-IsCurrent[1-Return-Past] | Account-HeadID-TransType-IsCurrent[1-Return-Current] | Account-HeadID-TransType-IsCurrent[2-Sales-Past]| ...
1 |1000 | 900 (500 + 400) |150 | 600[300+200+100] |2000
See SQL Fiddle with Demo
Any suggestion or input are most welcome!
Thanks
Suresh
Try this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(CONVERT(NVARCHAR(10),AccountHeadID) + N'-' + TransType + N'-' + CONVERT(NVARCHAR(10),IsPast))
from Trans
group by AccountHeadID, TransType, IsPast
order by AccountHeadID, TransType, IsPast
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
--select #cols
set #query = 'SELECT CustID,' + #cols + '
from
(
SELECT
a.CustID,
CONVERT(NVARCHAR(10),AccountHeadID) + N''-'' + TransType + N''-'' + CONVERT(NVARCHAR(10),IsPast) Acct,
a.Amount
FROM Trans a
) x
pivot
(
sum(Amount)
for Acct in (' + #cols + ')
) p '
execute sp_executesql #query;
SQL Fiddle