combine 3 rows (with two columns) into columns (6) - per user - sql

I have something like that:
Name colA colB
row1 John a b
row2 John c d
row3 John e f
row4 Anna g h
row5 Anna i j
row6 Anna k l
and I would like to have something like that:
Name colA1 colB1 colA2 colB2 colA3 colB3
row1 John a b c d e f
row2 Anna g h i j k l
It will be always 3 rows per user.

You can use row_number() and conditional aggregation:
select name,
max(case when seqnum = 1 then a end) as a_1,
max(case when seqnum = 1 then b end) as b_1,
max(case when seqnum = 2 then a end) as a_2,
max(case when seqnum = 2 then b end) as b_2,
max(case when seqnum = 3 then a end) as a_3,
max(case when seqnum = 3 then b end) as b_3
from (select t.*
row_number() over (partition by name order by (select null)) as seqnum
from t
) t
group by name;

Related

SQL transpose multiple rows to different columns

I have SQL server data Table, like this:
ID
SYMPTOM1
SYMPTOM2
1
A
B
1
C
D
2
E
F
3
A
C
3
D
E
I'd like to obtain this:
ID
SYMPTOM1
SYMPTOM2
SYMPTOM3
SYMPTOM4
1
A
B
C
D
2
E
F
3
A
C
D
E
That should be an "easy" pivot, but I can't figure it out.
How could a SQL query be written?
*EDIT
Excuse me, I have neglected an important detail. The number of rows in the table is very large, some 500.000 so the IDs are very large.
You could do it as a self join - use row number to have another column that is either 1 or 2 (depending on how many times ID appeared), 1 should always appear, 2 sometimes appears, so left joining those rows with 2 onto those rows with 1 (based on the id) gives you the end result..
WITH x AS(
SELECT
t.ID,
t.SYMPTOM1,
t.SYMPTOM2,
ROW_NUMBER() OVER (PARTITION BY t.ID ORDER BY t.SYMPTOM1) as rn
FROM t
)
SELECT
*
FROM
x x1
LEFT JOIN x x2 ON x1.id = x2.id AND x1.rn = 1 AND x2.rn = 2
You can pivot on a row-number
SELECT
t.ID,
SYMPTOM1 = MAX(CASE WHEN t.rn = 1 THEN t.SYMPTOM1 END),
SYMPTOM2 = MAX(CASE WHEN t.rn = 1 THEN t.SYMPTOM2 END),
SYMPTOM3 = MAX(CASE WHEN t.rn = 2 THEN t.SYMPTOM1 END),
SYMPTOM4 = MAX(CASE WHEN t.rn = 2 THEN t.SYMPTOM2 END)
FROM (
SELECT
t.*,
rn = ROW_NUMBER() OVER (PARTITION BY t.ID ORDER BY (SELECT 1))
FROM YourTable t
) t
GROUP BY t.ID;

Spliting GROUP BY results into different columns

I have a column containing date ranges and the number of days passed associated to a specific ID (one to many), based on the number of records associated to it, I want those results split into columns instead of individual rows, so from this:
id_hr dd beg end
----------------------------------------
1 10 05/01/2019 15/01/2019
1 5 03/02/2019 08/02/2019
2 8 07/03/2019 15/03/2019
Could become this:
id_hr dd beg end dd beg end
--------------------------------- ---------------------
1 10 05/01/2019 15/01/2019 5 03/02/2019 08/02/2019
2 8 07/03/2019 15/03/2019
I did the same in a worksheet (pivot table) but the table became as slow as it could get, so I'm looking for a more friendly approach in SQL, I did a CTE which number the associated rows and then select each one and display them in new columns.
;WITH CTE AS(
SELECT PER_PRO, ID_HR, NOM_INC, rut_dv, dias_dur, INI, FIN,
ROW_NUMBER()OVER(PARTITION BY ID_HR ORDER BY SUBIDO) AS RN
FROM dbo.inf_vac WHERE PER_PRO = 201902
)
SELECT ID_HR, NOM_INC, rut_dv,
(case when rn = 1 then DIAS_DUR end) as DIAS_DUR1,
(case when rn = 1 then INI end) as INI1,
(case when rn = 1 then FIN end) as FIN1,
(case when rn = 2 then DIAS_DUR end) as DIAS_DUR2,
(case when rn = 2 then INI end) as INI2,
(case when rn = 2 then FIN end) as FIN2,
(case when rn = 3 then DIAS_DUR end) as DIAS_DUR3,
(case when rn = 3 then INI end) as INI3,
(case when rn = 3 then FIN end) as FIN3
FROM CTE
Which gets me each column on where it should be but not grouped. Using GROUP BY displays an error on the CTE select.
rn id_hr dd beg end dd beg end
----------------------------------- ------------------------
1 1 10 05/01/2019 15/01/2019 NULL NULL NULL
2 1 NULL NULL NULL 5 03/02/2019 08/02/2019
1 2 8 07/03/2019 15/03/2019 NULL NULL NULL
Is there any way to group them on the second select?
You have additional columns in the result set that are not in the query. However, this should work:
SELECT ID_HR,
max(case when rn = 1 then DIAS_DUR end) as DIAS_DUR1,
max(case when rn = 1 then INI end) as INI1,
max(case when rn = 1 then FIN end) as FIN1,
max(case when rn = 2 then DIAS_DUR end) as DIAS_DUR2,
max(case when rn = 2 then INI end) as INI2,
max(case when rn = 2 then FIN end) as FIN2,
max(case when rn = 3 then DIAS_DUR end) as DIAS_DUR3,
max(case when rn = 3 then INI end) as INI3,
max(case when rn = 3 then FIN end) as FIN3
FROM CTE
GROUP BY ID_HR;
Yes, you can GROUP BY all the non-CASE columns, and apply MAX to each of the CASE-expression columns.

SQL PIVOT table with MIN() and on Multiple columns

Here is the table structure
ID TypeX TypeXDesc XDate TypeCodeY
040001 3669 Unspecified Cat 2005-08-08 1
040001 3669 Unspecified Cat 2006-08-29 2
040001 37515 Tear Film 2005-08-08 1
040001 37999 Disor 2004-07-22 1
Transform above table INTO below USING PIVOT
ID TypeX_1 TypeXDesc_1 XDate_1 TypeCodeY_1 TypeX_2 TypeXDesc_2 XDate_2 TypeCodeY_2 TypeX_3 TypeXDesc_3 XDate_3 TypeCodeY_3
040001 3669 Unspecified Cat 2005-08-08 1 37515 Tear Film 2005-08-08 1 37999 Disor 2004-07-22 1
Look at the same TypeX code but XDate is different and we need to get Min(XDate) so first row is qualified not the second row.
You can accomplish this using conditional aggregation. In this case, you can enumerate the rows with within typex/id groups using row_number(). You can enumerate the groups with with typex/id using dense_rank().
Then, use conditional aggregation:
select t.id,
max(case when grpnum = 1 and seqnum = 1 then typex end) as typex_1,
max(case when grpnum = 1 and seqnum = 1 then TypeXDesc end) as TypeXDesc_1,
max(case when grpnum = 1 and seqnum = 1 then XDate end) as XDate_1,
max(case when grpnum = 1 and seqnum = 1 then TypeCodeY end) as TypeCodeY_1,
max(case when grpnum = 2 and seqnum = 1 then typex end) as typex_12,
max(case when grpnum = 2 and seqnum = 1 then TypeXDesc end) as TypeXDesc_2,
max(case when grpnum = 2 and seqnum = 1 then XDate end) as XDate_2,
max(case when grpnum = 2 and seqnum = 1 then TypeCodeY end) as TypeCodeY_3,
max(case when grpnum = 3 and seqnum = 1 then typex end) as typex_1,
max(case when grpnum = 3 and seqnum = 1 then TypeXDesc end) as TypeXDesc_3,
max(case when grpnum = 3 and seqnum = 1 then XDate end) as XDate_3,
max(case when grpnum = 3 and seqnum = 1 then TypeCodeY end) as TypeCodeY_3
from (select t.*,
row_number() over (partition by id, typex order by xdate as seqnum,
dense_rank() over (partition by id order by typex) as grpnum
from t
) t
group by id;

SQL Pivot View for Single Table

Have a table:
Nomber Sce SceValue
10 A a1b2c3
20 C d2v3b4
10 B 42b2c3
10 B 5978c3
20 A edr432
I need to create the following listing using a view where there all possible "Sce" and "SceValue" pairs are shown for each individual "Nomber" (9 pairs maximum):
Nomber Sce1 SceValue1 Sce2 SceValue2 ... Sce9 SceValue9
10 A a1b2c3 B 42b2c3 B 5978c3
20 C d2v3b4 A edr432
I would like to achieve this using a View. Is this possible?
You can use conditional aggregation:
select number,
max(case when seqnum = 1 then Sce end) as sce_1,
max(case when seqnum = 1 then SceValue end) as SceValue_1,
max(case when seqnum = 2 then Sce end) as sce_2,
max(case when seqnum = 2 then SceValue end) as SceValue_2,
. . .
max(case when seqnum = 9 then Sce end) as sce_9,
max(case when seqnum = 9 then SceValue end) as SceValue_9
from (select t.*,
row_number() over (partition by nomber order by sce) as seqnum
from t
) t
group by nomber;

how to get value x without code duplication

create table t(a int, b int);
insert into t values (1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3);
select * from t;
a | b
----------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
3 | 3
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(
max(case when a = 1 then b else 0 end)
+
max(case when b = 1 then a else 0 end)
) as x
from t
Is it possible to do something like this?
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(q + c) as x
from t
You can't use the ALIAS that was given on the same level of the SELECT clause.
You have two choices:
by using the expression directly
query:
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(max(case when a = 1 then b else 0 end) + max(case when b = 1 then a else 0 end)) as x
from t
by wrapping in a subquery
query:
SELECT q,
c,
q + c as x
FROM
(
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c
from t
) d
Also in SQLServer2005+ you can use CTE
;WITH cte AS
(
select max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c
from t
)
SELECT q, c, q + c as x
FROM cte
You can't do that unfortunately.
The ALIAS can not be used in the same level where you created them.
A temporary table is necessary, i think.