SQL pivot to flatten rows into columns - sql-server

I have a source table that looks like this:
+--------------+----------+------+------------+-----------+
| vehicleindex | parentid | year | make | model |
+--------------+----------+------+------------+-----------+
| 1 | 1 | 2007 | TOYOTA | SIENNA LE |
| 2 | 1 | 2005 | VOLKSWAGEN | JETTA GLS |
+--------------+----------+------+------------+-----------+
I'd like to select from this table such that the output looks like this:
+-------+--------+-----------+-------+------------+-----------+
| year1 | make1 | model1 | year2 | make2 | model2 |
+-------+--------+-----------+-------+------------+-----------+
| 2007 | TOYOTA | SIELLA LE | 2005 | VOLKSWAGEN | JETTA GLS |
+-------+--------+-----------+-------+------------+-----------+
How can I accomplish this on a SQL Server database with a pivot? There will always be either 1 or 2 vehicles in the source table. In the case where there's 1 vehicle, I would expect Year2, Make2 and Model2 to be NULL.

Similar to SQLZim's answer. Only difference is that the Window function Row_Number() is used just in case vehicleindex is not a consistent 1 and 2.
Select year1 = max(case when RN=1 then [year] end)
,make1 = max(case when RN=1 then make end)
,model1 = max(case when RN=1 then model end)
,year2 = max(case when RN=2 then [year] end)
,make2 = max(case when RN=2 then make end)
,model2 = max(case when RN=2 then model end)
From (
Select *
,RN = Row_Number() over (Partition By parentid Order By vehicleindex)
From YourTable
) A
Group By parentid
EDIT: Option 2 - Use PIVOT
Select *
From (
Select parentid
,item = concat(B.item,Dense_Rank() over (Partition By parentid Order By vehicleindex))
,value
From YourTable
Cross Apply ( values ('year' ,cast(Year as varchar(100)))
,('make' ,make)
,('model',model)
) B (item,value)
) A
Pivot (max(value) For [item] in ([year1],[make1],[model1],[year2],[make2],[model2]) ) p

Using conditional aggregation:
select
parentid
, year1 = max(case when vehicleindex=1 then [year] end)
, make1 = max(case when vehicleindex=1 then make end)
, model1 = max(case when vehicleindex=1 then model end)
, year2 = max(case when vehicleindex=2 then [year] end)
, make2 = max(case when vehicleindex=2 then make end)
, model2 = max(case when vehicleindex=2 then model end)
from t
group by parentid
returns:
+----------+-------+------------+-----------+-------+------------+-----------+
| parentid | year1 | make1 | model1 | year2 | make2 | model2 |
+----------+-------+------------+-----------+-------+------------+-----------+
| 1 | 2007 | TOYOTA | SIENNA LE | 2005 | VOLKSWAGEN | JETTA GLS |
| 2 | 2018 | TESLA | MODEL 3 | NULL | NULL | NULL |
+----------+-------+------------+-----------+-------+------------+-----------+
rextestder demo: http://rextester.com/ZTGXU25389
using this test data:
create table t (
vehicleindex int
, parentid int
, [year] int
, make varchar(32)
, model varchar(32)
);
insert into t values
(1,1,2007,'TOYOTA ','SIENNA LE')
, (2,1,2005,'VOLKSWAGEN','JETTA GLS')
, (1,2,2018,'TESLA','MODEL 3')

Unfortunately if you need to pivot 3 columns you need to create 3 separate pivots, then merge them in the end:
SELECT MAX(Year1) as 'Year1', MAX(Make1) as Make1, MAX(Model1) as 'Model1',
MAX(Year2) as 'Year2', MAX(Make2) as Make2, MAX(Model2) as 'Model2'
FROM
(
SELECT parentid,
[1] as 'Year1', NULL as Make1, NULL as 'model1',
[2] as 'Year2', NULL as Make2, NULL as 'model2'
FROM (
SELECT
parentid, vehicleindex, model, make , [year]
FROM #temp
) as s
PIVOT
(
max([year])
FOR vehicleindex IN ([1],[2])
) AS yearPvt
UNION
SELECT parentid,
NULL as 'Year1', [1] as Make1, NULL as 'model1',
NULL as 'Year2', [2] as Make2, NULL as 'model2'
FROM
(
SELECT
parentid, vehicleindex, model, make , [year]
FROM #temp
) as s
parentid, vehicleindex, model, make , [year]
FROM #temp
) as s
PIVOT
(
MAX([make])
FOR vehicleindex IN ([1],[2])
)AS makePvt
UNION
SELECT parentid,
NULL as 'Year1', NULL as Make1, [1] as 'model1',
NULL as 'Year2', NULL as Make2, [2] as 'model2'
FROM
(
SELECT
parentid, vehicleindex, model, make , [year]
FROM #temp
) as s
PIVOT
(
max([model])
FOR vehicleindex IN ([1],[2])
) AS modelPvt
) allPivots
Group BY parentid

Related

Convert a (<=4) x 2 SQL result set to 1 x 8 result set

I've spent much too long trying to figure out how TSQL pivoting works, so I'm posting this hoping someone that understands pivots can help me.
So here's a representative source result set:
Notice that there are only 3 rows, but that's still <=4.
| Code | Date |
|-------|------------|
| 12345 | 2018-01-01 |
| 67890 | NULL |
| 13579 | 2018-01-02 |
This is target result set I want from the source result set:
| Code_1 | Date_1 | Code_2 | Date_2 | Code_3 | Date_3 | Code_4 | Date_4 |
|--------|------------|--------|--------|--------|------------|--------|--------|
| 12345 | 2018-01-01 | 67890 | NULL | 13579 | 2018-01-02 | NULL | NULL |
Here's something that does literally what you're asking by joining 2 pivot queries together:
DECLARE #tbl TABLE (code INT, dt DATETIME)
INSERT INTO #tbl VALUES (12345, '1/1/2018')
INSERT INTO #tbl VALUES (67890, null)
INSERT INTO #tbl VALUES (13579, '1/1/2018')
SELECT SQ1.Code_1, SQ2.Code_1, SQ1.Code_2, SQ2.Code_2, SQ1.Code_3, SQ2.Code_3, SQ1.Code_4, SQ2.Code_4
FROM
(
SELECT [Code_1], [Code_2], [Code_3], [Code_4]
FROM
(
SELECT 'Code_' + CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS VARCHAR(3)) AS RowID, code FROM #tbl
) source
PIVOT
(
MAX(code)
FOR RowID IN ([Code_1], [Code_2], [Code_3], [Code_4])
)sq
)SQ1
CROSS APPLY
(
SELECT [Code_1], [Code_2], [Code_3], [Code_4]
FROM
(
SELECT 'Code_' + CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS VARCHAR(3)) AS RowID, dt FROM #tbl
) source
PIVOT
(
MAX(dt)
FOR RowID IN ([Code_1], [Code_2], [Code_3], [Code_4])
)sq
)SQ2
Outputs:
Code_1 Code_1 Code_2 Code_2 Code_3 Code_3 Code_4 Code_4
12345 2018-01-01 67890 NULL 13579 2018-01-01 NULL NULL
Assuming you want a max of 4 groups (8 columns).
You may also notice that in the Order By I use (Select null). Without a proper sequence key, there is no gtd of order.
Example
Select Code_1 = max(case when Col=1 then Code end)
,Date_1 = max(case when Col=1 then Date end)
,Code_2 = max(case when Col=2 then Code end)
,Date_2 = max(case when Col=2 then Date end)
,Code_3 = max(case when Col=3 then Code end)
,Date_3 = max(case when Col=3 then Date end)
,Code_4 = max(case when Col=4 then Code end)
,Date_4 = max(case when Col=4 then Date end)
From (
Select *
,Col = Row_Number() over (Order by (Select null))
From #YourTable
) A
Returns
Code_1 Date_1 Code_2 Date_2 Code_3 Date_3 Code_4 Date_4
12345 2018-01-01 67890 NULL 13579 2018-01-01 NULL NULL

Extending a Pivot

I have a Documents table and an Events table.
Documents table has ID and a bunch of other fields not relevant to
this question.
Events table has DocID, EventType, EventDate, and UserID.
A document may have zero or more Events of any of these EventTypes:
0 = Created
1 = Modified
2 = Submitted
3 = Approved
DocID | EventType | EventDate | UserID
-----------------------------------------------
1 | 0 | 1-2-2017 | 123
1 | 1 | 1-3-2017 | 456
1 | 1 | 1-4-2017 | 489
1 | 2 | 1-5-2017 | 357
2 | 0 | 1-6-2017 | 951
2 | 1 | 1-7-2017 | 654
2 | 2 | 1-8-2017 | 654
2 | 3 | 1-9-2017 | 357
Pivoting the Events table is easy enough:
SELECT DocID, [0] AS CreatedDate, [1] AS ModifiedDate,
[2] AS SubmittedDate, [3] AS ApprovedDate
FROM (SELECT DocID, EventType, EventDate FROM Events
WHERE DocID IS NOT NULL AND EventDate IS NOT NULL) AS DocEvents
PIVOT (MAX(EventDate) FOR EventType IN ([0], [1], [2], [3]))
AS DocEventsPivot
For my purposes, the most recent event of a given type is wanted, thus the MAX aggregate:
DocID | CreatedDate | ModifiedDate | SubmittedDate | ApprovedDate
-----------------------------------------------------------------
1 | 1-2-2017 | 1-4-2017 | 1-5-2017 | NULL
2 | 1-6-2017 | 1-7-2017 | 1-8-2017 | 1-9-2017
How can I get the UserID translated to CreatedBy, ModifiedBy, SubmittedBy, and ApprovedBy to correspond to the dates of the appropriate EventType?
I will not know the possible values of UserID in advance.
Desired Output:
DocID | CreatedDate | ModifiedDate | SubmittedDate | ApprovedDate | CreatedBy | ModifiedBy | SubmittedBy | ApprovedBy
---------------------------------------------------------------------------------------------------------------------
1 | 1-2-2017 | 1-4-2017 | 1-5-2017 | NULL | 123 | 489 | 357 | NULL
2 | 1-6-2017 | 1-7-2017 | 1-8-2017 | 1-9-2017 | 951 | 654 | 654 | 657
Rather than using PIVOT another solution is using OUTER APPLY.
CREATE TABLE #Documents (ID int)
CREATE TABLE #Events (DocID int, EventType int, EventDate date, UserID int)
INSERT INTO #Documents VALUES
(1),
(2)
INSERT INTO #Events VALUES
(1, 0, '1-2-2017', 123),
(1, 1, '1-3-2017', 456),
(1, 1, '1-4-2017', 489),
(1, 2, '1-5-2017', 357),
(2, 0, '1-6-2017', 951),
(2, 1, '1-7-2017', 654),
(2, 2, '1-8-2017', 654),
(2, 3, '1-9-2017', 357)
SELECT
DOC.ID AS 'DocID',
CRT.EventDate AS 'CreatedDate',
MFY.EventDate AS 'ModifiedDate',
SUB.EventDate AS 'SubmittedDate',
APR.EventDate AS 'ApprovedDate',
CRT.UserID AS 'CreatedBy',
MFY.UserID AS 'ModifiedBy',
SUB.UserID AS 'SubmittedBy',
APR.UserID AS 'ApprovedBy'
FROM
#Documents AS DOC
OUTER APPLY (SELECT TOP 1 EventDate, UserID FROM #Events WHERE DocID = DOC.ID AND EventType = 0 ORDER BY EventDate DESC) AS CRT
OUTER APPLY (SELECT TOP 1 EventDate, UserID FROM #Events WHERE DocID = DOC.ID AND EventType = 1 ORDER BY EventDate DESC) AS MFY
OUTER APPLY (SELECT TOP 1 EventDate, UserID FROM #Events WHERE DocID = DOC.ID AND EventType = 2 ORDER BY EventDate DESC) AS SUB
OUTER APPLY (SELECT TOP 1 EventDate, UserID FROM #Events WHERE DocID = DOC.ID AND EventType = 3 ORDER BY EventDate DESC) AS APR
DROP TABLE #Documents
DROP TABLE #Events
Try the following
CREATE TABLE #Events (DocID int, EventType int, EventDate date, UserID int)
INSERT INTO #Events VALUES
(1, 0, '1-2-2017', 123),
(1, 1, '1-3-2017', 456),
(1, 1, '1-4-2017', 489),
(1, 2, '1-5-2017', 357),
(1, 2, '1-4-2017', 666),
(2, 0, '1-6-2017', 951),
(2, 1, '1-7-2017', 654),
(2, 2, '1-8-2017', 654),
(2, 3, '1-9-2017', 357)
SELECT
DocID,
MAX(CASE WHEN EventType=0 THEN EventDate END) [CreatedDate],
MAX(CASE WHEN EventType=1 THEN EventDate END) [ModifiedDate],
MAX(CASE WHEN EventType=2 THEN EventDate END) [SubmittedDate],
MAX(CASE WHEN EventType=3 THEN EventDate END) [ApprovedDate],
MAX(CASE WHEN EventType=0 THEN LastUserID END) [CreatedBy],
MAX(CASE WHEN EventType=1 THEN LastUserID END) [ModifiedBy],
MAX(CASE WHEN EventType=2 THEN LastUserID END) [SubmittedBy],
MAX(CASE WHEN EventType=3 THEN LastUserID END) [ApprovedBy]
FROM
(
SELECT
DocID,
EventDate,
EventType,
LAST_VALUE(UserID)OVER(
PARTITION BY DocID,EventType
ORDER BY EventDate
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) LastUserID
FROM #Events
) q
GROUP BY DocID
DROP TABLE #Events
Or you can use IFF instead CASE. I prefer use CASE because it isn't block ELSE
SELECT
DocID,
MAX(IIF(EventType=0,EventDate,NULL)) [CreatedDate],
MAX(IIF(EventType=1,EventDate,NULL)) [ModifiedDate],
MAX(IIF(EventType=2,EventDate,NULL)) [SubmittedDate],
MAX(IIF(EventType=3,EventDate,NULL)) [ApprovedDate],
MAX(IIF(EventType=0,LastUserID,NULL)) [CreatedBy],
MAX(IIF(EventType=1,LastUserID,NULL)) [ModifiedBy],
MAX(IIF(EventType=2,LastUserID,NULL)) [SubmittedBy],
MAX(IIF(EventType=3,LastUserID,NULL)) [ApprovedBy]
FROM
(
SELECT
DocID,
EventDate,
EventType,
LAST_VALUE(UserID)OVER(
PARTITION BY DocID,EventType
ORDER BY EventDate
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) LastUserID
FROM #Events
) q
GROUP BY DocID

Rearranging a table in tsql

So I have a table with the following columns:
Type Test Min Max
-----------------------------
1 a 1 2
1 b Null Null
2 a 0 Null
2 b Null 1
Trying to get all of them like this
Type Test1 Test1 min Test1max Test2 Test2min Test2max
------------------------------------------------------------
1 a 1 2 b Null Null
2 a 0 Null b Null 1
Tried using unpivot first before I use pivot but it's still giving duplicate tests and removing null any help with this is much appreciated
Need null values to show up as well
Select type, result
(
From
Select type, min
From table t
)
Unpivot
(result for minmax in (min1)
)
Select
Thanks
using row_number() and conditional aggregation:
select
[Type]
, Test_1 = max(case when rn = 1 then Test end)
, Test_1_Min = max(case when rn = 1 then Min end)
, Test_1_Max = max(case when rn = 1 then Max end)
, Test_2 = max(case when rn = 2 then Test end)
, Test_2_Min = max(case when rn = 2 then Min end)
, Test_2_Max = max(case when rn = 2 then Max end)
from (
select *
, rn = row_number() over (partition by [Type] order by [Test])
from t
) as s
group by s.Type
rextester demo: http://rextester.com/BKL48976
returns:
+------+--------+------------+------------+--------+------------+------------+
| Type | Test_1 | Test_1_Min | Test_1_Max | Test_2 | Test_2_Min | Test_2_Max |
+------+--------+------------+------------+--------+------------+------------+
| 1 | a | 1 | 2 | b | NULL | NULL |
| 2 | a | 0 | NULL | b | NULL | 1 |
+------+--------+------------+------------+--------+------------+------------+
select *
from table t1
join table t2
on t1.type = t2.type
and t1.test < t2.test

Create the summary table SQL

I have a table of product stock (ProductStockTemp):
ProductStockId | ProductCode | ProductName | ProductStockClosingBalance
-------------------------------------------------------------------------
1 | A1 | A2 | 200
2 | B1 | B2 | 0
3 | A1 | A2 | 100
4 | C1 | C2 | -400
5 | B1 | B2 | 700
6 | C1 | C2 | 0
7 | D1 | D2 | 0
8 | D1 | D2 | 0
I would like to create a table like that
ProductCode | ProductName | TypeA | TypeB
------------------------------------------
A1 | A2 | 200 | 100
B1 | B2 | 0 | 700
C1 | C2 | -400 | 0
D1 | D2 | 0 | 0
It means the first product record will be the product type A and the latest record will be the product type B. I think i have to select the latest product record first. However, the problem arises when the product appears one time then it will choose this record. In this case, I want the product type B will be 0 since the first will be the product type A and the latest will be the type B. My query to select the latest product record in the table
select
p.ProductCode,
p.ProductName,
p.ProductStockClosingBalance
from
ProductStockTemp p
where
p.ProductStockId = (select MAX(q.ProductStockId)
from ProductStockTemp q
where p.ProductCode = q.ProductCode)
order by
p.ProductCode
I'm really stuck right now!
Most obvius in my opinion for this case:
;with cteStock as
(
select
p.ProductCode,
p.ProductName,
p.ProductStockClosingBalance,
row_number() over(partition by p.ProductCode, p.ProductName order by p.ProductStockId asc) rn_asc,
row_number() over(partition by p.ProductCode, p.ProductName order by p.ProductStockId desc) rn_desc,
from ProductStockTemp p
)
select
s.ProductCode, s.ProductName,
sum(case when s.rn_asc = 1 then s.ProductStockClosingBalance else 0) as TypeA,
sum(case when s.rn_desc = 1 and s.rn_asc != s.rn_desc then s.ProductStockClosingBalance else 0) as TypeB
from cteStock s
group by s.ProductCode, s.ProductName
but it's no good to have such a denormalized table with no info about period and names stored right here.
s.rn_asc != s.rn_desc - for the case when there is only one row.
Not sure about default zero but it's up to you and your task definition.
I suggest you to learn SQL GROUP BY Statement
SELECT ProductCode, ProductName, min(ProductStockClosingBalance) as TypeA, Max(ProductStockClosingBalance) as TypeB
FROM ProductStockTemp p
GROUP BY ProductCode, ProductName
This query does what you want.
declare #prod table(ProductStockId int, ProductCode varchar(10), ProductName varchar(10), ProductStockClosingBalance int)
insert #prod values
( 1 , 'A1' , 'A2' , 200),
( 2 , 'B1' , 'B2' , 0),
( 3 , 'A1' , 'A2' , 100),
( 4 , 'C1' , 'C2' , -400),
( 5 , 'B1' , 'B2' , 700),
( 6 , 'C1' , 'C2' , 0),
( 7 , 'D1' , 'D2' , 0),
( 8 , 'D1' , 'D2' , 0 )
;with prod as(
select *
,ROW_NUMBER() over(partition by productcode order by ProductStockId) rn1
,ROW_NUMBER() over(partition by productcode order by ProductStockId desc) rn2
from #prod
)
select productcode,ProductName,
(select ProductStockClosingBalance from prod p1 where p.productcode=p1.ProductCode and p1.rn1=1) TypeA,
ProductStockClosingBalance typeB
from prod p
where rn2=1
Results:
productcode ProductName TypeA typeB
A1 A2 200 100
B1 B2 0 700
C1 C2 -400 0
D1 D2 0 0
use below query
;With ctef as (select ProductCode , ProductName , ProductStockClosingBalance
row_number()over(partition by ProductCode , ProductName ordered by ProductStockId asc) as r from ProductStock)
, ctef1 as ( select * from ctef where r =1)
, ctel as (select ProductCode , ProductName , ProductStockClosingBalance
row_number()over(partition by ProductCode , ProductName ordered by ProductStockId desc)as r2 from ProductStock )
, ctel1 as ( select * from ctel where r =1)
select f.ProductCode , f.ProductName ,f.ProductStockClosingBalance as typea ,nullif(l.ProductStockClosingBalance ,f.ProductStockClosingBalance ) as typeB
from ctef1 f left join ctel1 s
on f.ProductCode =s.ProductCode and f.ProductName =s.ProductName
if using sql servere 2012 or above Analytic Functions FIRST_VALUE (Transact-SQL) and LAST_VALUE (Transact-SQL) can be used .

Update several columns with latest values from another table

Here's the data:
[ TABLE_1 ]
id | prod1 | date1 | prod2 | date2 | prod3 | date3 |
---|--------|--------|--------|--------|--------|-------|
1 | null | null | null | null | null | null |
2 | null | null | null | null | null | null |
3 | null | null | null | null | null | null |
[ TABLE_2 ]
id | date | product |
-----|-------------|-----------|
1 | 20140101 | X |
1 | 20140102 | Y |
1 | 20140103 | Z |
2 | 20141201 | data |
2 | 20141201 | Y |
2 | 20141201 | Z |
3 | 20150101 | data2 |
3 | 20150101 | data3 |
3 | 20160101 | X |
Both tables have other columns not listed here.
date is formatted: yyyymmdd and datatype is int.
[ TABLE_2 ] doesn't have empty rows, just tried to make sample above more readable.
Here's the Goal:
I need to update [ TABLE_1 ] prod1,date1,prod2,date2,prod3,date3
with product collected from [ TABLE_2 ] with corresponding date values.
Data must be sorted so that "latest" product becomes prod1,
2nd latest product will be prod2 and 3rd is prod3.
Latest product = biggest date (int).
If dates are equal, order doesn't matter. (see id=2 and id=3).
Updated [ TABLE_1 ] should be:
id | prod1 | date1 | prod2 | date2 | prod3 | date3 |
---|--------|----------|--------|----------|--------|----------|
1 | Z | 20140103 | Y | 20140102 | X | 20140101 |
2 | data | 20141201 | Y | 20141201 | Z | 20141201 |
3 | X | 20160101 | data2 | 20150101 | data3 | 20150101 |
Ultimate goal is to get the following :
[ TABLE_3 ]
id | order1 | order2 | order3 | + Columns from [ TABLE_1 ]
---|--------------------|----------------------|------------|--------------------------
1 | 20140103:Z | 20140102:Y | 20140103:Z |
2 | 20141201:data:Y:Z | NULL | NULL |
3 | 20160101:X | 20150101:data2:data3 | NULL |
I have to admit this exceeds my knowledge and I haven't tried anything.
Should I do it with JOIN or SELECT subquery?
Should I try to make it in one SQL -clause or perhaps in 3 steps,
each prod&date -pair at the time ?
What about creating [ TABLE_3 ] ?
It has to have columns from [ TABLE_1 ].
Is it easiest to create it from [ TABLE_2 ] -data or Updated [ TABLE_1 ] ?
Any help would be highly appreciated.
Thanks in advance.
I'll post some of my own shots on comments.
After looking into it (after my comment), a stored procedure would be best, that you can call to view the data as a pivot, and do away with TABLE_1. Obviously if you need to make this dynamic, you'll need to look into dynamic pivots, it's a bit of a hack with CTEs:
CREATE PROCEDURE DBO.VIEW_AS_PIVOTED_DATA
AS
;WITH CTE AS (
SELECT ID, [DATE], 'DATE' + CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY [DATE] DESC) AS VARCHAR) AS [RN]
FROM TABLE_2)
, CTE2 AS (
SELECT ID, PRODUCT, 'PROD' + CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY [DATE] DESC) AS VARCHAR) AS [RN]
FROM TABLE_2)
, CTE3 AS (
SELECT ID, [DATE1], [DATE2], [DATE3]
FROM CTE
PIVOT(MAX([DATE]) FOR RN IN ([DATE1],[DATE2],[DATE3])) PIV)
, CTE4 AS (
SELECT ID, [PROD1], [PROD2], [PROD3]
FROM CTE2
PIVOT(MAX(PRODUCT) FOR RN IN ([PROD1],[PROD2],[PROD3])) PIV)
SELECT A.ID, [PROD1], [DATE1], [PROD2], [DATE2], [PROD3], [DATE3]
FROM CTE3 AS A
JOIN CTE4 AS B
ON A.ID=B.ID
Construction:
WITH ranked AS (
SELECT [id]
,[date]
,[product]
,row_number() over (partition by id order by date desc) rn
FROM [sistemy].[dbo].[TABLE_2]
)
SELECT id, [prod1],[date1],[prod2],[date2],[prod3],[date3]
FROM
(
SELECT id, type+cast(rn as varchar(1)) col, value
FROM ranked
CROSS APPLY
(
SELECT 'date', CAST([date] AS varchar(8))
UNION ALL
SELECT 'prod', product
) ca(type, value)
) unpivoted
PIVOT
(
max(value)
for col IN ([prod1],[date1],[prod2],[date2],[prod3],[date3])
) pivoted
You need to take a few steps to achive the aim.
Rank your products by date:
SELECT [id]
,[date]
,[product]
,row_number() over (partition by id order by date desc) rn
FROM [sistemy].[dbo].[TABLE_2]
Unpivot your date and product columns into one column. You can use UNPIVOT OR CROSS APPLY statements. I prefer CROSS APPLY
SELECT id, type+cast(rn as varchar(1)) col, value
FROM ranked
CROSS APPLY
(
SELECT 'date', CAST([date] AS varchar(8))
UNION ALL
SELECT 'prod', product
) ca(type, value)
or the same result using UNPIVOT
SELECT id, type+cast(rn as varchar(1)) col, value
FROM (
SELECT [id],
rn,
CAST([date] AS varchar(500)) date,
CAST([product] AS varchar(500)) prod
FROM ranked) t
UNPIVOT
(
value FOR type IN (date, product)
) unpvt
and at last you use PIVOTE and get a result.

Resources