SQL Challenge concatenate rows to the end of every group - sql-server

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;

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.

How to pivot table from multiple column to multiple rows

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

Operand data type image is invalid for max operator in SQL Server 2014

I have a query that works in dev 2008 but does not work in test 2014.
I need the query returned in the exact format due to display requiremnts, but I don't know how to convert it. I tried casting and converting.
The error message i get on 2014 when i run the query is
Msg 8117, Level 16, State 1, Line 3
Operand data type image is invalid for max operator.
Here is the query:
SELECT Max(CASE
WHEN rn % 3 = 0 THEN itemtransactionbatchid
END) AS ItemTransactionBatchId,
Max(CASE
WHEN rn % 3 = 0 THEN referencebytes
END) AS ReferenceBytes,
Max(CASE
WHEN rn % 3 = 0 THEN description
END) AS Description,
Max(CASE
WHEN rn % 3 = 1 THEN itemtransactionbatchid
END) AS ItemTransactionBatchId1,
Max(CASE
WHEN rn % 3 = 1 THEN referencebytes
END) AS ReferenceBytes1,
Max(CASE
WHEN rn % 3 = 1 THEN description
END) AS Description1,
Max(CASE
WHEN rn % 3 = 2 THEN itemtransactionbatchid
END) AS ItemTransactionBatchId2,
Max(CASE
WHEN rn % 3 = 2 THEN referencebytes
END) AS ReferenceBytes2,
Max(CASE
WHEN rn % 3 = 2 THEN description
END) AS Description2
FROM (SELECT a.itemtransactionbatchid,
a.referencebytes,
b.description,
( ROW_NUMBER()
OVER(
ORDER BY CASE WHEN b.code='SIGOPRT' THEN 1 WHEN b.code='SIGDRVR' THEN 2 WHEN b.code='SIGTRANS' THEN 3 WHEN b.code='SIGRECV' THEN 4 WHEN b.code='SIGN' THEN 5 WHEN b.code='SIGSUPR' THEN 6 ELSE 0 END, b.code, (SELECT NULL)) - 1 ) rn
FROM [Transaction].itemtransactionref a (NOLOCK)
JOIN [Transaction].transactionreftype b
ON a.transactionreftypeid = b.transactionreftypeid
WHERE b.code IN( 'SIGDRVR', 'SIGN', 'SIGOPRT', 'SIGRECV',
'SIGSUPR', 'SIGTRANS' )
AND a.itemtransactionbatchid = xxxxxxx
AND referencebytes IS NOT NULL)x
GROUP BY rn / 3
Change the datatype of the image column to VARBINARY(MAX); MAX on that type is supported.
This does not give any errors:
CREATE TABLE #t(entr VARBINARY(MAX));
SELECT MAX(entr) FROM #t;

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.

How to select PIVOT for this table?

I have a table like this:
Id Code Value
----------- -------------------- -----------
1 A 100
2 B 100
3 C 220
4 A 150
5 C 300
6 D 120
7 E 120
And this is my expectation:
Code1 Value1 Code2 Value2 Code3 Value3
----------- ----------- ----------- ----------- ----------- -----------
A 100 B 100 C 220
A 150 C 300 D 120
E 120
Should I use PIVOT to this case (and how to do this) or normal select query?
This isn't with pivot either, but in case you need to group the items into groups of 3 ordered by ID and the ID has gaps, like sometimes happen, this should do it:
select
ROW,
max(case when COL = 0 then Code end) as Code1,
max(case when COL = 0 then Value end) as Value1,
max(case when COL = 1 then Code end) as Code2,
max(case when COL = 1 then Value end) as Value2,
max(case when COL = 2 then Code end) as Code3,
max(case when COL = 2 then Value end) as Value3
FROM (
select
(row_number() over (order by ID)-1) % 3 as COL,
(row_number() over (order by ID)-1) / 3 as ROW,
Code,
Value
from
data
) X
group by ROW
This calculates row and column numbers based on row_number that increases over ID, and then uses group by to split the results into groups of 3.
SQL Fiddle
Perhaps PIVOT is not usefull here.
SELECT t1.code as code1, t1.value as value1,
t2.code as code2, t2.value as value2,
t3.code as code3, t3.value as value3
FROM tab as t1
left join tab as t2
ON t1.id = t2.id - 1
left join tab as t3
ON t2.id = t3.id - 1
where ((t1.id-1) % 3) = 0

Resources