Create row as a column in stored procedure - sql-server

I have the following three tables and I want to so as last table using a stored procedure. Can anyone tell me how I can join them?
Disc:
DiscId DiscName
1 a
2 b
3 c
DiscDetail:
DiscDetailId DiscId DiscDetailName Percentage
1 1 p 5
2 1 q 10
3 2 r 12
4 2 s 11
5 2 t 13
6 3 u 19
7 3 v 20
Pur:
PurId DiscId
1 1
2 2
3 1
4 1
I want to show data as follows:
PurId p q r s t
1 5 10
2 12 11 13
3 5 10
4 5 10

Use dynamic pivoting:
First declare all the columns :
DECLARE #Columns AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #Columns = STUFF((SELECT distinct ',' + QUOTENAME(c.DiscDetailName )
FROM DiscDetail c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
Create dynamic pivot:
set #query = 'SELECT PurId, ' + #Columns + ' from
(
select Pur.PurId
, DiscDetail.Percentage
, DiscDetail.DiscDetailName
from Pur
inner join Disc on Pur.DiscID = Disc.DiscId
inner join DiscDetail on DiscDetail.ID = Disc.DiscId
) x
pivot
(
max(Percentage)
for DiscDetailName in (' + #Columns + ')
) p '
execute(#query)

SELECT PurId ,
CASE WHEN DD.DiscDetailName ='P' THEN DD.Percentage END 'P',
CASE WHEN DD.DiscDetailName ='q' THEN DD.Percentage END 'q',
CASE WHEN DD.DiscDetailName ='r' THEN DD.Percentage END 'r',
CASE WHEN DD.DiscDetailName ='s' THEN DD.Percentage END 's',
CASE WHEN DD.DiscDetailName ='t' THEN DD.Percentage END 't'
FROM
(
SELECT * FROM DISC_DETAIL D
INNER JOIN CHILD_TABLE CT ON DD.DiscId =CT.DiscId
)DD
Try above query.

Related

SQL How to join rows as column headers in a Pivot?

I have the following 2 tables:
TagNames:
TagName
TagIndex
Name1
0
Name2
1
Name3
2
TagValues:
DateAndTime
TagIndex
Val
2023-02-08 09:31:51.000
0
0
2023-02-08 09:31:51.000
1
10
2023-02-08 09:31:51.000
2
20
2023-02-08 09:32:01.000
0
1
2023-02-08 09:32:01.000
1
11
2023-02-08 09:32:01.000
2
21
Using this query I managed to fetch the rows as cols
WITH Tags AS (
SELECT
T.[TagIndex],
T.[DateAndTime],
T.[Val]
FROM
[dbo].[TagValues] T
INNER JOIN [dbo].[TagNames] N
ON T.TagIndex = N.TagIndex
)
SELECT *
FROM
Tags
PIVOT (MAX([Val]) FOR [TagIndex] IN ([0], [1], [2])) P
ORDER BY DateAndTime
;
Obtaining as result something like this:
DateAndTime
0
1
2
2023-02-08 09:31:51.000
0
10
20
2023-02-08 09:32:01.000
1
11
21
What I want to do is substitute the column headers with TagName in the first table
You can do this with dynamic SQL, as I describe in this article:
DECLARE #TagNameCols nvarchar(max), #sql nvarchar(max);
SELECT #TagNameCols = STRING_AGG(QUOTENAME(tn.TagName),',')
FROM dbo.TagNames AS tn
WHERE TagIndex IN (SELECT TagIndex FROM dbo.TagValues);
SELECT #sql = N'WITH Tags AS (
SELECT
N.[TagName],
T.[DateAndTime],
T.[Val]
FROM
[dbo].[TagValues] T
INNER JOIN [dbo].[TagNames] N
ON T.TagIndex = N.TagIndex
)
SELECT *
FROM
Tags
PIVOT (MAX([Val]) FOR [TagName] IN (' + #TagNameCols + N')) P
ORDER BY DateAndTime
;';
EXEC sys.sp_executesql #sql;
Working db<>fiddle example

How can I unpivot and then pivot my table so the columns become rows and one column becomes a row?

How can I accomplish this with unpivot and pivot.
I've seen this question asked before and has a solution with case statement and union all
In SQL, how can I count the number of values in a column and then pivot it so the column becomes the row?
and here PIVOT/UNPIVOT multiple rows and columns but I have 20 rows and 24 columns and the query would become very long and I suspect inefficient. Does anyone know how I can do this with unpivot and pivot or is case and union all the only viable option?
Hour A B C D E ... Z
-----------------------------------------
0 4 2 3 0 6 2
1 3 5 7 1 8 7
2 2 6 1 1 4 3
3 2 2 0 3 0 2
4 3 9 6 2 2 8
...
23 6 5 2 3 8 6
Field 0 1 2 3 ...23
-------- -- -- -
A 2 0 2 2 4
B 7 2 8 1 6
....
Z 6 7 7 3 8
This is what I've tried in terms of pivot but I didn't get far:
select B,[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23] from CTE2
pivot(
sum(A)
for hour in ([0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23])) as pvt;
Just to clarify, the numbers in the table are just random numbers I've put to simulate data, they aren't transposed as they should be.
Well, I know you say you've solved it so this probably isn't necessary and you can feel free to use whatever answer you currently have, but here's an example of how you could approach this problem in general.
IF OBJECT_ID('tmpTable', 'U') IS NOT NULL DROP TABLE tmpTable;
CREATE TABLE tmpTable (i INT, a INT, b INT, c INT, d INT);
INSERT tmpTable VALUES (1,69,69,10,1)
, (2,5,0,2,3)
, (3,5,5,5,5)
, (4,1,2,3,4);
DECLARE #applycols NVARCHAR(MAX);
SELECT #applycols = STUFF(
(SELECT ',(' + QUOTENAME(COLUMN_NAME) + ', ''' + COLUMN_NAME + ''')'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tmpTable'
AND COLUMN_NAME <> 'i'
FOR XML PATH ('')),1,1,'');
DECLARE #aggcols NVARCHAR(MAX) = '';
SELECT #aggcols += ', MAX(CASE WHEN i = ' + CAST(i AS NVARCHAR(255)) + ' THEN piv.colval END) ' + QUOTENAME(CAST(i AS NVARCHAR(255)))
FROM tmpTable;
DECLARE #SQL NVARCHAR(MAX) = 'SELECT piv.col' + #aggcols + '
FROM tmpTable
CROSS APPLY (VALUES ' + #applycols + ') piv(colval, col)
GROUP BY piv.col;';
EXEC(#SQL);
DROP TABLE tmpTable;
Essentially, it's using dynamic SQL to determine all the columns/values and then using a simple CROSS APPLY / MAX(CASE... to get all the values.

SQL Server Pivot Multiple Values

Using SQL Server 2012.
I have the following table. The Style and colour are passed as a parameter:
Style Colour Size Whse Stock Sales 4WeekSales ATP
ABC123 AS12 10 London 2 3 6 7
ABC123 AS12 12 London 4 6 8 10
ABC123 AS12 14 New York 6 8 9 12
ABC123 AS12 10 New York 7 5 7 5
But need the data to look like this with the sizes along the top:
Whse 10 12 14
Lon
Stock 2 4 6
Sales 3 6 8
4WeekSales 6 8 9
ATP 7 10 12
New York
Stock 7 6
Sales 5 8
4WeekSales 7 9
ATP 5 12
Points to note - the size field needs to be dynamic - sometimes it can be 6 /8/10/12, sometimes it can be XS/S/M/L etc
Also their are more than two whse's - this is just an example.
I did make a start in T-SQL:
use SafetyStock
go
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns += N', p.' + QUOTENAME(Size)
FROM (SELECT p.Size FROM dbo.vw_optimums AS p
GROUP BY p.Size) AS x;
SET #sql = N'
SELECT SKU, Style,' + STUFF(#columns, 1, 2, '') + '
FROM
(
SELECT SKU, Style, p.Size, p.SAFETYSTOCK
FROM dbo.vw_optimums AS p
) AS j
PIVOT
(
SUM(SAFETYSTOCK) FOR Size IN ('
+ STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')
+ ')
) AS p;';
PRINT #sql;
EXEC sp_executesql #sql;
However, this works but only pivots on the stock - how do I also pivot by Sales\4WeekSales\ATP and also groupb by the whse?
Thank you in advance.
This is my latest code. If I take the SEQNO out it works, but I need this so the sizes appear along the top correctly e.g. S / M / L / XL / XXL etc or 6/ 8 / 10 / 12
DECLARE #SizeColums VARCHAR(MAX)
DECLARE #Seq Integer
SELECT
#SizeColums = COALESCE(#SizeColums + ',','') + QUOTENAME([Size]),
#Seq = SEQNO
FROM vw_optimums1
GROUP BY [Size],[SEQNO]
ORDER BY [SEQNO]
DECLARE #Sql NVARCHAR(MAX) = N'
SELECT Whse,
[Types],' +
#SizeColums + '
FROM (SELECT * FROM vw_optimums1) t
UNPIVOT (
[Type]
FOR [Types] IN ([Stock],[LWSALES],[L4WSALES],[ATP]) ) up
PIVOT (
MAX([Type])
FOR [Size] IN (' + #SizeColums + ')
) p
'
EXEC sp_executesql #sql;
About the closest I can get you is this.
DECLARE #SizeColums VARCHAR(MAX)
SELECT #SizeColums = COALESCE(#SizeColums + ',','') + QUOTENAME([Size])
FROM vw_optimums
GROUP BY [Size]
DECLARE #Sql NVARCHAR(MAX) = N'
SELECT Whse,
[Types],' +
#SizeColums + '
FROM (SELECT * FROM vw_optimums
) t
UNPIVOT (
[Type]
FOR [Types] IN ([Stock],[Sales],[4WeekSales],[ATP]) ) up
PIVOT (
MAX([Type])
FOR [Size] IN (' + #SizeColums + ')
) p
'
This actually uses UNPIVOT first to get the breakdown by size, then pivots based on size.
You'll get a result like this based on the sample data.
Whse Types 10 12 14
-------- -------------- ----------- ----------- -----------
London 4WeekSales 6 8
London ATP 7 10
London Sales 3 6
London Stock 2 4
New York 4WeekSales 7 9
New York ATP 5 12
New York Sales 5 8
New York Stock 7 6

How to keep column order same in dynamic pivot

I have below mentioned table :
drn RecNum Name Value
----------------------------------------------
1 1 ad1_pk 1
2 1 ad1_address1 P.O. Box 5036
3 1 ad1_address2 NULL
4 1 ad1_address3 NULL
5 1 ad1_ctyfk 56
6 1 ad1_postalcode 80155-5036
7 1 ad1_active Y
8 1 ad1_irstat A
9 1 ad1_irdata NULL
10 1 ad1_at1fk 1
1 2 ad1_pk 2
2 2 ad1_address1 1871 S. Broadway
3 2 ad1_address2 NULL
4 2 ad1_address3 NULL
5 2 ad1_ctyfk 1
6 2 ad1_postalcode 80210
7 2 ad1_active Y
8 2 ad1_irstat A
9 2 ad1_irdata NULL
10 2 ad1_at1fk 1
I am creating the pivot using the below mentioned query:
declare #var nvarchar(max)
declare #sql nvarchar(max)
set #var = stuff((select distinct ',' + name from temp
for xml path('')),1,1,'') -- **this is giving distinct column list but the order of columns get changed..**
set #sql = 'select * from temp
pivot(max(value) for name in (' + #var + ')) as pvt'
exec sp_executesql #sql
Is there a way to keep the order of the columns unchanged? I want the order of columns listed in #var to be same as in the table.
Add a GROUP BY and an ORDER BY clause (to replace the DISTINCT) where you build your column list as follows:
set #var = stuff((select ',' + min(name) from temp GROUP BY drn ORDER BY drn
for xml path('')),1,1,'')
And don't forget the the necessary aggregation (I've used MIN()). Thanks #Ionic.
This is because you're using a DISTINCT in your SELECT query. If you look at the execution plan, you can see DISTINCT SORT operation. This sorts your result based on the DISTINCT columns you specify, in this case it's Name:
To retain the order, you can try this:
set #var = stuff((
select ',' + name
from(
select
name,
drn,
rn = row_number() over(partition by name order by drn)
from temp
)t
where rn = 1
order by drn
for xml path('')),
1,1,'')

Transposing columns to rows using UNPIVOT

I have a table that for some reason has hardcoded values like so:
Row ID QtyC1 QtyC2 QtyC3 QtyC4 QtyN1 QtyN2 QtyN3 QtyN4
100 10 5 8 9 11 12 5 6
101 9 11 12 5 6 10 4 9
The table has 35 columns and around 12k records (meaning around 500k values) and is being added to and amended constantly.
I am trying to transpose this in a view into:
Row ID Year Period Val
100 C 1 10
100 C 2 5
100 C 3 8
100 C 4 9
100 N 1 11
100 N 2 12
100 N 3 5
100 N 4 6
So far I have managed to split it out into single values using this query:
SELECT Row ID, YP, Val
FROM (SELECT Row ID
, QtyC1 AS C1
, QtyC2 AS C2
, QtyC3 AS C3
, QtyC4 AS C4
, QtyN1 AS N1
, QtyN2 AS N2
, QtyN3 AS N3
, QtyN4 AS N4
FROM MyTable
) SUB
UNPIVOT (Val FOR YP IN (C1,C2,C3,C4,N1,N2,N3,N4)) AS PVT
This is getting me a single identifying value (eg C1) but how can I split it so I have a numeric period and a single character for the year (1 and C)?
I can see it might be possible just splitting up the string into two parts but I was hoping for a cleaner way if possible.
You can easily split the YP string using LEFT(), RIGHT(), SUBSTRING(), etc. My suggestion would be how you are handling your UNPIVOT. It looks like you have a lot of columns to UNPIVOT so my suggestion might be to implement dynamic SQL to perform this query. You would do it this way:
declare #colsUnpivot varchar(max),
#query AS NVARCHAR(MAX),
#cols varchar(max)
select #colsUnpivot = stuff((select ','
+quotename(replace(C.name, 'Qty', ''))
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name like 'Qty%'
for xml path('')), 1, 1, '')
select #cols = stuff((select ','
+quotename(C.name) + ' as ' + replace(C.name, 'Qty', '')
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name like 'Qty%'
for xml path('')), 1, 1, '')
set #query
= 'select rowid,
left(YP, 1) YP,
cast(right(YP, len(YP) - 1) as int) period,
Val
from
(
select rowid, ' + #cols + '
from yourtable
) x1
unpivot
(
val for YP IN (' + #colsUnpivot + ')
) u'
exec(#query)
see SQL Fiddle with Demo
Why would this seem unclean?
SELECT Row ID, left(YP, 1) as year, cast(right(yp, 1) as int) as period, Val
FROM (SELECT Row ID
, QtyC1 AS C1
, QtyC2 AS C2
, QtyC3 AS C3
, QtyC4 AS C4
, QtyN1 AS N1
, QtyN2 AS N2
, QtyN3 AS N3
, QtyN4 AS N4
FROM MyTable
) SUB
UNPIVOT (Val FOR YP IN (C1,C2,C3,C4,N1,N2,N3,N4)) AS PVT

Resources