How to pivot table from multiple column to multiple rows - sql-server

CodeDt CodeHeader Item Qty Type Remark Attachment
LK4-033502 RK-K-LK4-032438 IA01001023 2.00 TPR002 2 1.jpeg
LK4-033502RK RK-K-LK4-032438 IA01001023RK 2.00 NULL IA01001023 NULL
Above is my data from Sql server table (using 2008 R2). I want to make it one row only. Here is my expected result:
CodeDt CodeHeader Item NewItem Qty
LK4-033502 RK-K-LK4-032438 IA01001023 IA01001023RK 2.00
How can I achieve that? Here is the relation:
Row 1 Item = Row 2 Remark,
Row 1 Code DT = Row 2 CodeDt+'RK'

There are several solutions
1) Using JOIN. It assumes that Type field is null for rows with CodeDt+'RK'
select
a.CodeDt, a.CodeHeader, a.Item, b.Item, a.Qty
from
myTable a
join myTable b on a.Item = b.Remark
where
a.Type is not null
2) Conditional aggregation
select
max(case when rn = 1 then CodeDt end)
, CodeHeader
, max(case when rn = 1 then Item end)
, max(case when rn = 2 then Item end)
, max(case when rn = 1 then Qty end)
from (
select
*, rn = row_number() over (partition by CodeHeader order by CodeDt)
from
myTable
) t
group by CodeHeader

Related

Query for making multiple indefinite rows into one row with multiple columns

I have two tables
ID
ID2
1
1
1
2
2
3
3
4
3
5
And the second one
ID2
Code
Date1
1
A
01/01/2023
2
B
01/02/2023
3
C
01/03/2023
4
A
01/01/2023
5
D
01/15/2023
The second table has more columns that I need to include, but I'm only including two (Code and Date1) for the sake of brevity.
What I need is to unite everything based on the ID of the first table. So it would look something like
ID1
ID2-1
Code-1
Date1-1
ID2-2
Code-2
Date1-2
1
1
A
01/01/2023
2
B
01/02/2023
2
3
C
01/03/2023
NULL
NULL
NULL
3
4
A
01/01/2023
5
D
01/15/2023
In these examples one ID repeats up to two times in the second table, but the second table can have an indefinite amount of records tied to an ID from the first table. Meaning it might be Code-10, or Code-20, or maybe more or less. I need to do this in a pretty big query for a report I'm doing, so these are not the only fields that will be in the final result, but for this data specifically I only use two tables that have a very similar structure to the one I'm describing here. Any help will be appreciated.
To build on T N's code, this puppy builds a "dynamic" 30 column wide pivot.
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = N'
SELECT
A.ID
[COLUMNS]
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ID2) AS RowNum
FROM TableA
) A
JOIN TableB B
ON B.ID2 = A.ID2
GROUP BY A.ID
ORDER BY A.ID'
SELECT #SQL = REPLACE(#SQL, '[COLUMNS]', (
SELECT CONCAT(N'
, MAX(CASE WHEN A.RowNum = ', x.sort, ' THEN B.ID2 END) AS [ID2-', x.sort, N']
, MAX(CASE WHEN A.RowNum = ', x.sort, ' THEN B.Code END) AS [Code-', x.sort, N']
, MAX(CASE WHEN A.RowNum = ', x.sort, ' THEN B.Date END) AS [Date-', x.sort, N']')
FROM (
SELECT TOP 30 row_number() OVER(ORDER BY (SELECT NULL)) AS sort
FROM sys.objects so
) x
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'))
SELECT #SQL
EXEC(#SQL)
You can accomplish this by using a combination of a windowed ROW_NUMBER() number function and conditional aggregation.
First, ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) is used to assign sequences number to each row with the same ID. The results are then be joined with your second table and a GROUP BY ID is applied. Finally, conditional aggregation functions of the form MAX(CASE WHEN ... THEN .. END) can be used to select the proper value for each column, with specific row numbers assigned to specific columns.
Something like:
SELECT
A.ID,
MAX(CASE WHEN A.RowNum = 1 THEN B.ID2 END) AS [ID2-1],
MAX(CASE WHEN A.RowNum = 1 THEN B.Code END) AS [Code-1],
MAX(CASE WHEN A.RowNum = 1 THEN B.Date END) AS [Date-1],
MAX(CASE WHEN A.RowNum = 2 THEN B.ID2 END) AS [ID2-2],
MAX(CASE WHEN A.RowNum = 2 THEN B.Code END) AS [Code-2],
MAX(CASE WHEN A.RowNum = 2 THEN B.Date END) AS [Date-2]
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ID2) AS RowNum
FROM TableA
) A
JOIN TableB B
ON B.ID2 = A.ID2
GROUP BY A.ID
ORDER BY A.ID
Results:
ID
ID2-1
Code-1
Date-1
ID2-2
Code-2
Date-2
1
1
A
2023-01-01
2
B
2023-01-02
2
3
C
2023-01-03
null
null
null
3
4
A
2023-01-01
5
D
2023-01-15
See this db<>fiddle.
If you need to support an arbitrary number of records per ID instead of just two, you will then need to dive into the world of dynamic SQL.

SQL Challenge concatenate rows to the end of every group

I have the following table and I need to flatten the data.
From here
table 1
id val1 val2 ....valn
-----------------------------------
1 a z
1 b x
1 c v
2 a w
2 b q
..n
To here
id val1_1 val2_1 ....valn_1 val1_2 val2_2 ... valn_2 .... val1_n...valn_n
-----------------------------------
1 a z b x
2 a w b q
..n
Any ideas?
I have done something using cursors, however it is a little nasty and It has some bugs. Pivot?
Yes you need to a pivot, but you also need to calculate a row-number to pivot against. However, given this is a multi-column pivot, it's probably easier to just use conditional aggregation.
I suggest you pick a proper ordering for the row-number
SELECT
id,
val1_1 = MAX(CASE WHEN t.rn = 1 THEN t.val1 END),
val2_1 = MAX(CASE WHEN t.rn = 1 THEN t.val2 END),
val3_1 = MAX(CASE WHEN t.rn = 1 THEN t.val3 END),
-- ...........
val1_2 = MAX(CASE WHEN t.rn = 2 THEN t.val1 END),
val2_2 = MAX(CASE WHEN t.rn = 2 THEN t.val2 END),
val3_2 = MAX(CASE WHEN t.rn = 2 THEN t.val3 END),
-- ...........
FROM (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t.rn ORDER BY (SELECT 1))
FROM table1 t
) t
GROUP BY t.id;

SQL PIVOT on a variable amount of rows

I have the following data on a MSSQL Server:
Item No_ Unit Of Measure Qty Per Base UoM Rounding Precision
000001 PIECE 1 1.0
000001 PALLET 100 1.0
000001 BOX 12 1.0
000002 KG 1 1.0
000002 TON 1000 0.001
I am trying to get the following output:
Item No_ UoM1 Qty pb UoM1 RP1 UoM2 Qty pb UoM2 RP2 UoM3 Qty pb UoM3 RP3
000001 PIECE 1 1.0 PALLET 100 1.0 BOX 12 1.0
000002 KG 1 1.0 TON 1000 0.0001 NULL NULL NULL
I tried achieving this using the PIVOT operator but it does not seem te be correct.
What is the correct way to achieve my desired output?
For trully generic solution you need to use Dynamic-SQL.
But you say you want starting point, so I give you one. You can use:
LiveDemo
WITH cte AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY Item_No_ ORDER BY (SELECT 1)) AS rn
FROM #mytable
)
select Item_No_,
max(case when rn = 1 then Unit_Of_Measure end) UoM1,
max(case when rn = 1 then Qty_Per_Base_UoM end) [Qty pb UoM1],
max(case when rn = 1 then Rounding_Precision end) RP1,
max(case when rn = 2 then Unit_Of_Measure end) UoM2,
max(case when rn = 2 then Qty_Per_Base_UoM end) [Qty pb UoM2],
max(case when rn = 2 then Rounding_Precision end) RP2,
max(case when rn = 3 then Unit_Of_Measure end) UoM3,
max(case when rn = 3 then Qty_Per_Base_UoM end) [Qty pb UoM3],
max(case when rn = 3 then Rounding_Precision end) RP3,
max(case when rn = 4 then Unit_Of_Measure end) UoM4,
max(case when rn = 4 then Qty_Per_Base_UoM end) [Qty pb UoM4],
max(case when rn = 4 then Rounding_Precision end) RP4
from cte
group by Item_No_;
The point is that if your number of units is known in advance you can create hardcoded columns from 1 .. n.
For more info search Dynamic Pivot multiple columns. It is achievable, but first try to crack this solution.

SQL Server : convert rows to one row

I know SQL Server Pivot, but this time I think I will need something more complex then a simple pivoted value.
Here it is an SQL fiddle example.
So I have a table where I have maximum 3 rows for one startcity - endcity combination.
In a query I need to get these combinations in just one row, and I need just those combinations, where there are 3 rows.
Please advice,
You can do this as a crosstab like this.
with NumberedValues as
(
SELECT EndCity
, StartDate
, EndDate
, Price
, ROW_NUMBER() OVER(PARTITION BY StartCity, EndCIty ORDER BY StartDate) as RowNum
FROM [dbo].[BestPrice]
)
SELECT EndCity,
max(CASE WHEN RowNum = 1 THEN StartDate END) as StartDate1,
max(CASE WHEN RowNum = 1 THEN Enddate END) as EndDate1,
max(CASE WHEN RowNum = 1 THEN Price END) as Price1,
max(CASE WHEN RowNum = 2 THEN StartDate END) as StartDate2,
max(CASE WHEN RowNum = 2 THEN Enddate END) as EndDate2,
max(CASE WHEN RowNum = 2 THEN Price END) as Price2,
max(CASE WHEN RowNum = 3 THEN StartDate END) as StartDate3,
max(CASE WHEN RowNum = 3 THEN Enddate END) as EndDate3,
max(CASE WHEN RowNum = 3 THEN Price END) as Price3
FROM NumberedValues
group by EndCity

SQL Server - How to display master details data in columns

I have two tables, to be concise let’s call them TableA and TableB. This is the schema:
TableA
ID – int
Name varchar(50)
TableB
ID – int
TableA_Fk – int
Value varchar(50)
Each record in table A can have at most 9 records in table B. I want to be able to retrieve the data in a columnar form:
TableA-Name, TableB-Value1, … TableB-Value9
Is this possible using queries? Thanks!
You could do something like:
SELECT rank() OVER (ORDER BY tableA_FK) as rank, tableA_fk, value
INTO #temp
FROM TableB b
ORDER BY rank
SELECT a.Name,
CASE WHEN t.rank = 1 THEN t.Value ELSE NULL END AS TableB-Value1,
CASE WHEN t.rank = 2 THEN t.Value ELSE NULL END AS TableB-Value2,
CASE WHEN t.rank = 3 THEN t.Value ELSE NULL END AS TableB-Value3,
.... (etc.)
FROM TableA a
INNER JOIN #temp t ON a.Id = t.tableA_fk
You need Sql Server 2005 or up.
Sorry, but I don't have Sql Server (or the time) to test this well. Hope this gives you an idea and helps.
You will require a LEFT JOIN and a PIVOT table
This should do it, in addition to be DBRM independant.
SELECT A.Name
, SUM(CASE WHEN B.Value = 1 THEN 1 ELSE NULL END) AS B_Value_1
, SUM(CASE WHEN B.Value = 2 THEN 2 ELSE NULL END) AS B_Value_2
, SUM(CASE WHEN B.Value = 3 THEN 3 ELSE NULL END) AS B_Value_3
, SUM(CASE WHEN B.Value = 4 THEN 4 ELSE NULL END) AS B_Value_4
, SUM(CASE WHEN B.Value = 5 THEN 5 ELSE NULL END) AS B_Value_5
, SUM(CASE WHEN B.Value = 6 THEN 6 ELSE NULL END) AS B_Value_6
, SUM(CASE WHEN B.Value = 7 THEN 7 ELSE NULL END) AS B_Value_7
, SUM(CASE WHEN B.Value = 8 THEN 8 ELSE NULL END) AS B_Value_8
, SUM(CASE WHEN B.Value = 9 THEN 9 ELSE NULL END) AS B_Value_9
FROM A
INNER JOIN B ON B.TableA_FK = A.ID
GROUP BY A.Name
ORDER BY A.Name

Resources