SQL Server: Turning rows into columns - sql

I'm trying to make the following structure
ROW | GROUP | ORDER | COL_NAME | VALUE
---------------------------------------------
1 | 10 | 10 | FIRST_COL | Value1
2 | 10 | 10 | FIRST_COL | Value2
3 | 10 | 10 | FIRST_COL | Value3
4 | 10 | 20 | SECOND_COL| Val1
5 | 10 | 20 | SECOND_COL| Val2
6 | 20 | 10 | THIRD_COL | Opt3
...
into
FIRST_COL | SECOND_COL | THIRD_COL
-----------------------------------------------------
Value1 | Val1 | Opt3
Value2 | Val2 |
Value3 | |
What I currently have:
declare #cols varchar(max),
#query varchar(max)
select #cols = stuff((select ',' + quotename([COL_NAME])
from mt
group by [COL_NAME]
for xml path(''), type).value('.', 'varchar(max)'), 1, 1, '');
set #query = 'select ' + #cols + ' from (
select [COL_NAME], [VALUE]
from mt
) x
pivot (
min([VALUE])
for [COL_NAME] in (' + #cols + ')
) p
';
execute(#query);
The current code only shows the minimum values (since it's set to min([VALUES])), so only Value1, Val1 and Opt3 would be shown, but my question here is, how can I modify the code so I get the appropriate table/view?
Thanks!

I believe you need this:
set #query = 'select ' + #cols + ' from (
select [COL_NAME], [VALUE], ROW_NUMBER() OVER (PARTITION BY [COL_NAME] ORDER BY (SELECT NULL)) as seqnum
from mt
) x
pivot (
min([VALUE])
for [COL_NAME] in (' + #cols + ')
) p
';
The pivot uses all the columns in the subquery. You just need one to distinguish among the rows with the same column name.

Something like this?
Declare #YourTable table ([ROW] int,[GROUP] int,[ORDER] int,[COL_NAME] varchar(50),[VALUE] varchar(50))
Insert Into #YourTable Values
(1,10,10,'FIRST_COL' ,'Value1'),
(2,10,10,'FIRST_COL' ,'Value2'),
(3,10,10,'FIRST_COL' ,'Value3'),
(4,10,20,'SECOND_COL','Val1'),
(5,10,20,'SECOND_COL','Val2'),
(6,20,10,'THIRD_COL' ,'Opt3')
;with cteBase as (
Select *
,RowNr = Row_Number() over (Partition By [COL_NAME] Order by [ROW])
From #YourTable
)
Select First_Col = max(case when [COL_NAME]='FIRST_COL' then Value else '' end)
,Second_Col = max(case when [COL_NAME]='SECOND_COL' then Value else '' end)
,Third_Col = max(case when [COL_NAME]='THIRD_COL' then Value else '' end)
From cteBase
Group By RowNr
Returns
First_Col Second_Col Third_Col
Value1 Val1 Opt3
Value2 Val2
Value3

Try it like this:
DECLARE #tbl TABLE([ROW] INT,[GROUP] INT,[ORDER] INT,[COL_NAME] VARCHAR(100),VALUE VARCHAR(100));
INSERT INTO #tbl VALUES
(1,10,10,'FIRST_COL','Value1')
,(2,10,10,'FIRST_COL','Value2')
,(3,10,10,'FIRST_COL','Value3')
,(4,10,20,'SECOND_COL','Val1')
,(5,10,20,'SECOND_COL','Val2')
,(6,20,10,'THIRD_COL','Opt3');
WITH Sorted AS
(
SELECT tbl.*
,ROW_NUMBER() OVER(PARTITION BY tbl.[COL_NAME] ORDER BY tbl.[ROW]) AS SortInx
FROM #tbl AS tbl
)
SELECT MAX(CASE WHEN Sorted.[COL_NAME]='FIRST_COL' THEN Sorted.[VALUE] END) AS FIRST_COL
,MAX(CASE WHEN Sorted.[COL_NAME]='SECOND_COL' THEN Sorted.[VALUE] END) AS SECOND_COL
,MAX(CASE WHEN Sorted.[COL_NAME]='THIRD_COL' THEN Sorted.[VALUE] END) AS THIRD_COL
FROM Sorted
GROUP BY SortInx

As he stated, you need to use RowNumber() to populate your Row column, on your COL_NAME, so your data will look like this.
ROW | GROUP | ORDER | COL_NAME | VALUE
---------------------------------------------
1 | 10 | 10 | FIRST_COL | Value1
2 | 10 | 10 | FIRST_COL | Value2
3 | 10 | 10 | FIRST_COL | Value3
1 | 10 | 20 | SECOND_COL| Val1
2 | 10 | 20 | SECOND_COL| Val2
1 | 20 | 10 | THIRD_COL | Opt3
...
Then, just include the column in your SQL
set #query = 'select [Row], ' + #cols + ' from (
select [Row],[COL_NAME], [VALUE]
from mt
) x
pivot (
min([VALUE])
for [COL_NAME] in (' + #cols + ')
) p
';
execute(#query);
Then, you have
ROW FIRST_COL SECOND_COL THIRD_COL
1 Value1 Val1 Opt3
2 Value2 Val2 NULL
3 Value3 NULL NULL

Related

How to combine multiple SQL rows into columns dynamically

I have a table with
+-------+-------+-----------------+
| P1_ID | P2_ID | Relationship_ID |
+-------+-------+-----------------+
| 1 | 21 | 3 |
| 1 | 32 | 3 |
| 2 | 45 | 2 |
| 2 | 65 | 1 |
| 3 | 98 | 3 |
| 3 | 94 | 4 |
+-------+-------+-----------------+
I want the final table to look like:
+-------+--------+--------+------+------+
| P1_ID | P2_ID1 | P2_ID2 | RID1 | RID2 |
+-------+--------+--------+------+------+
| 1 | 21 | 32 | 3 | 3 |
| 2 | 45 | 65 | 2 | 1 |
| 3 | 98 | 94 | 3 | 4 |
+-------+--------+--------+------+------+
I am not sure which direction to go with this. I am trying to use a pivot but I can not seem to make it work. Maybe I am doing it wrong.
You can use conditional aggregation for this. This isn't exactly what you stated for output because the ordering of your data is a little funky. But this should point you in the right direction.
declare #Something table
(
P1_ID int
, P2_ID int
, Realationship_ID int
)
insert #Something values
(1,21,3)
, (1,32,3)
, (2,45,2)
, (2,65,1)
, (3,98,3)
, (3,94,4)
select P1_ID
, P2_ID1 = MAX(Case when RowNum = 1 then P2_ID end)
, P2_ID2 = max(case when RowNum = 2 then P2_ID end)
, RID1 = MAX(Case when RowNum = 1 then Realationship_ID end)
, RID2 = max(case when RowNum = 2 then Realationship_ID end)
from
(
select *
, RowNum = ROW_NUMBER() over(partition by s.P1_ID order by Realationship_ID)
from #Something s
) x
group by x.P1_ID
--EDIT--
Here is a fully dynamic solution for this. I switched to using a temp table because a table variable would be out of scope for dynamic sql. Obviously in your situation you would be using a persistent table. This will order the output by P1_ID and the columns within each row by Realationship_ID.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
P1_ID int
, P2_ID int
, Realationship_ID int
)
insert #Something values
(1,21,3)
, (1,32,3)
, (2,45,2)
, (2,65,1)
, (3,98,3)
, (3,94,4)
;
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by P1_ID order by P1_ID';
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *
, RowNum = ROW_NUMBER() over(partition by s.P1_ID order by Realationship_ID)
from #Something s
)
select P1_ID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a cross join E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then P2_ID end) as P2_ID' + CAST(N as varchar(6)) + CHAR(10) +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Realationship_ID end) as RID' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from #Something
group by P1_ID
order by COUNT(*) desc
)
declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
exec sp_executesql #SqlToExecute

Convert rows to column by date with pivot

I have read the stuff on MS pivot tables and I am still having problems getting this correct.
Data
wh_id | saledate | qty |
105 | 20190901 | 134.000000 |
105 | 20190902 | 190.000000 |
105 | 20190903 | 148.500000 |
105 | 20190904 | 157.500000 |
105 | 20190905 | 209.500000 |
I would like it to come out as a pivot table, like this:
wh_id | 1 | 2 | 3 | 4 | 5 |
105 | 134 | 190 |148.5 | 157.5 | 209.5 |
this the code :
DECLARE
#cols nvarchar(max)='' ,
#query nvarchar(max)=''
SET #cols = STUFF((SELECT ',' + QUOTENAME(DATEPART(dd, saledate))
FROM sales
WHERE month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
GROUP BY saledate
ORDER BY saledate ASC
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'');
set #query = 'SELECT [wh_id], ' + #cols + '
from
(
select [wh_id], QUOTENAME(DATEPART(dd, saledate)) saledate,qty
from sales where month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
) x
pivot
(
sum(qty)
for [saledate] in (' + #cols + ')
) p '
execute(#query);
but the result is like this
wh_id | 1 | 2 | 3 | 4 | 5 |
105 | 1 | 2 | 3 | 4 | 5 |
just change the source part from the above query remove QUOTENAME applied over saledate and try executing the query you will find the expected output.
set #query = 'SELECT [wh_id], ' + #cols + '
from
(
select [wh_id], DATEPART(dd, saledate) saledate,qty
from sales where month(saleDATE)=9 and year(saleDATE)=2019 and wh_id=105
) x
pivot
(
sum(qty)
for [saledate] in (' + #cols + ') ) p '
select wh_id,
max(case when rn = 1 then qty end) '1',
max(case when rn = 2 then qty end) '2',
max(case when rn = 3 then qty end) '3',
max(case when rn = 4 then qty end) '4',
max(case when rn = 5 then qty end) '5'
from
(
select wh_id,qty,
row_number() over(partition by wh_id order by qty) rn
from YourTableName
) src
group by wh_id
OutPut:-
Note:- Instead of using Pivot ...you can use this simple query...using ...case when and then with max aggregate function...
In above example....i'm forgot add decimal value in Table....

How distribute SQL results horizontally?

I have a table in SQL Server like this(all columns are numerics):
Id1 | Id2 | Number | Qty
----+-----+--------+-------
1 | 1 | 100001 | 100
1 | 2 | 100002 | 110
1 | 3 | 100003 | 120
2 | 1 | 100004 | 130
2 | 2 | 100005 | 200
2 | 3 | 100006 | 300
2 | 4 | 100007 | 400
and i want the result distributed in 2 more columns, like this (without Id's):
Number1 | Qty1 | Number2 | Qty2 | Number3 | Qty3
--------+-------|---------|-------|---------|------
100001 | 100 | 100002 | 110 | 100003 | 120
100004 | 130 | 100005 | 200 | 100006 | 300
100007 | 400 | null | null | null | null
Is it possible?
Thanks.
You can try something like this:
SELECT CASE WHEN id2 = 1 THEN number ELSE '' END AS 'Number1',
CASE WHEN id2 = 1 THEN Qty ELSE '' END AS 'Qty1',
CASE WHEN id2 = 2 THEN number ELSE '' END AS 'Number2',
CASE WHEN id2 = 2 THEN Qty ELSE '' END AS 'Qty2',
CASE WHEN id2 = 3 THEN number ELSE '' END AS 'Number3',
CASE WHEN id2 = 3 THEN Qty ELSE '' END AS 'Qty3'
FROM tablename
The only thing that comes to mind is a recursive CTE. that will allow you to play with the n values in Id2. Are you sure the Id2 value for your last row is correct with a value of 4?
what do you need is a PIVOT. Here is the link :
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017
is this useful.?
DECLARE #T AS TABLE
(
id1 INT,
id2 int ,
Number INT,
Qty Int
)
Insert into #T
SELECT 1,1,100001, 100 Union All
SELECT 1,2,100002, 110 Union All
SELECT 1,3,100003, 120 Union All
SELECT 2,1,100004, 130 Union All
SELECT 2,2,100005, 200 Union All
SELECT 2,3,100006, 300 Union All
SELECT 2,4,100007, 400
;with cte1 as
(
select *,
LEAD(Number) OVER(partition by id1 order by id2) as leadingNumber,
LEAD(Qty) OVER(partition by id1 order by id2) as LeadingQty,
ROW_NUMBER()over(partition by id1 order by (select null)) as rowNum
from #T
)
,
cte2
As
(
select
c1.id1,c1.Id2,
c1.Number As Number1,c1.Qty as Qty1,c1.leadingNumber As Number2, c1.LeadingQty as Qty2
,LEAD(leadingNumber) OVER(partition by id1 order by id2) as Number3,
LEAD(LeadingQty) OVER(partition by id1 order by id2) as Qty3
from cte1 c1
)
Select Number1,Qty1,Number2,Qty2,Number3,Qty3
from cte2 where id2%3=1
Ok. After a few researchs, i found a solution to generate dynamic columns and using PIVOT i solved my problem. Thanks to all for the tips.
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX), #Param1 AS int
SET #cols = STUFF((SELECT ',' + QUOTENAME(number) FROM View_GRTLinhasGRTLinhasTela WHERE id1 = #Param1 FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')
SET #query = 'SELECT *
FROM(
SELECT p.number, p.qty FROM View_GRTLinhasGRTLinhasTela p
WHERE p.id1 = #Param1) AS sourcetable
PIVOT (
SUM(qty)
FOR number IN (' + #cols + ') ) AS pivottable'
EXEC sp_executesql #query

Pivot table to turn rows into columns

I currently run the query
SELECT [PriceAttributeID]
,[PriceID]
,[AttributeID]
,[PriceAttributeComparator]
,[PriceAttributeMin]
,[PriceAttributeMax]
FROM [PriceAttribute]
Which gives the output
1 2 1 1 S NULL
2 3 1 1 M NULL
3 4 1 1 L NULL
4 5 1 1 L NULL
5 5 2 1 Black NULL
I would like to get the output (where _Comp, _Min and _Max relate to PriceAttributeComparator, PriceAttributeMin and PriceAttributeMax)
PriceID 1_Comp 1_Min 1_Max 2_Comp 2_Min 2_Max
2 1 S NULL NULL NULL NULL
3 1 M NULL NULL NULL NULL
4 1 L NULL NULL NULL NULL
5 1 L NULL 1 Black NULL
The same query would also be expected to have 1_ and 2_ prefixes as 4_, 5_, 19_ and 32_ or any other indeterminate number of ID's based on what is in the table at the time.
I have attempted a PIVOT table, but i am new to them and haven't the first clue on how to create what it is i am looking to do.
Part of the problem you are probably having with the PIVOT function is due to the fact you have multiple columns that you want to apply the function to. If you want to use the PIVOT function, then I would suggest first unpivoting the columns PriceAttributeComparator, PriceAttributeMin and PriceAttributeMax. When you unpivot the data you will no longer have multiple columns, you will have multiple rows, then you can apply the pivot to all of the appropriate values.
You did not specify what version of SQL Server you are using but you can use CROSS APPLY with a UNION ALL to unpivot the columns:
select priceid,
col = cast(attributeid as varchar(10))+'_'+ col,
value
from
(
select PriceID,
AttributeID,
comp = cast(PriceAttributeComparator as varchar(10)),
[min] = cast(PriceAttributeMin as varchar(10)),
[max] = cast(PriceAttributeMax as varchar(10))
from PriceAttribute
) d
cross apply
(
select 'comp', comp union all
select 'min', [min] union all
select 'max', [max]
) c (col, value)
See Demo. This process will convert your data into the following format:
| PRICEID | COL | VALUE |
-----------------------------
| 2 | 1_comp | 1 |
| 2 | 1_min | S |
| 2 | 1_max | (null) |
| 3 | 1_comp | 1 |
| 3 | 1_min | M |
| 3 | 1_max | (null) |
Once the data is in multiple rows, then you can apply the PIVOT function to the values in col:
select priceid,
[1_comp], [1_min], [1_max], [2_comp], [2_min], [2_max]
from
(
select priceid,
col = cast(attributeid as varchar(10))+'_'+ col,
value
from
(
select PriceID,
AttributeID,
comp = cast(PriceAttributeComparator as varchar(10)),
[min] = cast(PriceAttributeMin as varchar(10)),
[max] = cast(PriceAttributeMax as varchar(10))
from PriceAttribute
) d
cross apply
(
select 'comp', comp union all
select 'min', [min] union all
select 'max', [max]
) c (col, value)
) src
pivot
(
max(value)
for col in ([1_comp], [1_min], [1_max], [2_comp], [2_min], [2_max])
) piv;
See SQL Fiddle with Demo.
The above versions work great if you have a known number of values but if the values are unknown, then you will need to use dynamic SQL to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(cast(attributeid as varchar(10))+'_'+ col)
from
(
select distinct attributeid
from priceattribute
) d
cross apply
(
select 'comp', 1 union all
select 'min', 2 union all
select 'max', 3
) c (col, so)
group by attributeid, col, so
order by attributeid, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT priceid, ' + #cols + '
from
(
select priceid,
col = cast(attributeid as varchar(10))+''_''+ col,
value
from
(
select PriceID,
AttributeID,
comp = cast(PriceAttributeComparator as varchar(10)),
[min] = cast(PriceAttributeMin as varchar(10)),
[max] = cast(PriceAttributeMax as varchar(10))
from PriceAttribute
) d
cross apply
(
select ''comp'', comp union all
select ''min'', [min] union all
select ''max'', [max]
) c (col, value)
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo. These solutions will give a result:
| PRICEID | 1_COMP | 1_MIN | 1_MAX | 2_COMP | 2_MIN | 2_MAX |
----------------------------------------------------------------
| 2 | 1 | S | (null) | (null) | (null) | (null) |
| 3 | 1 | M | (null) | (null) | (null) | (null) |
| 4 | 1 | L | (null) | (null) | (null) | (null) |
| 5 | 1 | L | (null) | 1 | Black | (null) |
It might be simplest to do this using conditional aggregation rather than pivot:
SELECT PriceID,
max(case when AttributeID = 1 then PriceAttributeComparator end) as comp_1,
max(case when AttributeID = 1 then PriceAttributeMin end) as min_1,
max(case when AttributeID = 1 then PriceAttributeMax end) as max_1,
max(case when AttributeID = 2 then PriceAttributeComparator end) as comp_2,
max(case when AttributeID = 2 then PriceAttributeMin end) as min_2,
max(case when AttributeID = 2 then PriceAttributeMax end) as max_2
FROM PriceAttribute pa
group by PriceId;

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