How can I pivot 2 rows into columns?
ID|col_1|col_2|col_3|col_4
1 |a |A |Q |5000
1 |a |B |Q |8000
2 |a |A |R |4000
2 |a |B |T |6000
Into:
ID|col_1|A_col3|A_col4|B_col3|B_col4
1 |a |Q |5000 |R |4000
2 |a |Q |8000 |T |6000
where the values in col_2 is now the column headnings and values of col_3 and col_4 are the aggerated in the pivot.
I tried this but stuck on the next step and does not give wanted outouts:
select * from
(
select ID, col_1, col_2, col_3, col_4 from tableA
) as A
pivot (max(col_3) for col_2 in ('A','B'))
Just use conditional aggregation:
select id,
max(case when col_2 = 'A' then col_3 end) as a_col3,
max(case when col_2 = 'A' then col_4 end) as a_col4,
max(case when col_2 = 'B' then col_3 end) as b_col3,
max(case when col_2 = 'B' then col_4 end) as b_col4
from tablea
group by id;
I tried this but stuck on the next step ...
Consider below approach
select *
from tableA
pivot (max(col_3) as col3, max(col_4) as col4 for col_2 in ('A', 'B'))
if applied to sample data in your question - output is
Related
I have the following tableA:
column A | column B | column C
-------------------------------------
1 |10 |a
2 |10 |b
3 |10 |c
4 |10 |d
5 |20 |a
6 |20 |b
7 |20 |c
8 |20 |d
9 |30 |a
10 |30 |b
11 |30 |c
12 |30 |d
I want to get this output:
column A-10 | column A-20 | column C
-------------------------------------
1 |5 |a
2 |6 |b
3 |7 |c
4 |8 |d
I tried this SQL query:
From tableA select column A, column C WHERE column B = 10
Which works and gives me the expected results. However if I add a second WHERE clauses
From tableA select column A, column C WHERE column B = 10 AND column B = 20
I get an empty return. How to the correct output?
If I understand correctly, you can use aggregation and window functions:
select max(case when b = 10 then a end) as a_10,
max(case when b = 20 then a end) as a_20,
c
from (select t.*,
row_number() over (partition by c, b order by a) as seqnum
from t
) t
group by c, seqnum;
You should use join operator here.
select
t1.A as a10
,t2.A as a20
,t1.C as c
from (
select column A, column C From tableA WHERE column B = 10
) t1
inner join (
select column A, column C From tableA WHERE column B = 20
) t2
on t1.C = t2.C
You can't have both 10 AND 20 which is why it is blank when it is run.
You can however have 10 OR 20.
Swap out AND for OR and it should get you the result you're looking for.
Correct query:
select tab1.A as a10 ,tab2.A as a20 ,tab1.C as c
from (
select column A, column C From tableA WHERE column B = 10
) tab1,
(
select column A, column C From tableA WHERE column B = 20
) tab2
where tab.C = tab.C
For example I have data like this like this
date | col_1 | Col_2 | Col_3 | Col_4
---------------------------------------------
20021 | 1 | a | null | a
20022 | 2 | a | null | a
20023 | 3 | a | null | a
20024 | 4 | a | 4.5 | a
20031 | 1 | a | 11 | b
20032 | 2 | a | 2 | b
20033 | 3 | a | 9 | b
20034 | 4 | a | 11 | b
what I need is when value in Col_3 is null and Col_1 is not 4,
then select value in Col_3 where Col_1 = 4, that have the same
I tried using this case statement:
select col_2, date, col_1, col_4,
case when col_3 is null and col_1 != 1
then (select col_3 from table s where s.date = 4
and s.col_1= seg.col_1 and s.col_4= seg.col_4
and left(s.date,4) = left(seg.date,4))
else seg.col_3
end as col_3
from table seg
but for some reason it's not doing what I need it to do
I need it to change the results of the table above to become like this:
date | col_1 | Col_2 | Col_3 | Col_4
---------------------------------------------
20021 | 1 | a | 4.5 | a
20022 | 2 | a | 4.5 | a
20023 | 3 | a | 4.5 | a
20024 | 4 | a | 4.5 | a
20031 | 1 | a | 11 | b
20032 | 2 | a | 2 | b
20033 | 3 | a | 9 | b
20034 | 4 | a | 11 | b
Maybe use an OUTER APPLY (or CROSS APPLY if you can guarantee quarter 4 is there) to always have quarter 4 available per year per col4 then just use it where you have to.
select col_2, date, col_1, col_4,
case when col_3 is null and col_1 != 4
then backfill.col_3
else seg.col_3
end as col_3
from table seg
outer apply (select col_3 from table where col_1 = 4 and left(date,4) = left(seg.date,4)) and col_4 = seg.col_4) backfill
If you'd rather stick with CASE, this should work.
case when col_3 is null and col_1 != 4
then (select col_3 from table s where s.col_1 = 4
and s.col_4 = seg.col_4
and left(s.date,4) = left(seg.date,4))
else seg.col_3
Just use window functions and coalesce():
select date, col1, col2,
coalesce(max(case when col1 = 4 then col3 end) over (partition by col4) as col3,
col4
from t;
For example, say I have a table (id is letter):
letter|color |number
a |green |2
a |blue |3
b |red |3
b |blue |4
b |yellow|1
c |red |9
c |blue |5
What I want is to transform it to:
letter|color_1|color_2|color_3|number_1|number_2|number_3
a |green |blue | |2 |3 |
b |red |blue |yellow |3 |4 |1
c |red |blue | |9 |5 |
What type of SQL transformation is this? My boss said it is something done frequently but I've never seen it before? Also, how would you do it?
This is a pivot query. If you know that you want three sets of columns, then you can use conditional aggregation.
The problem in SQLite is that you don't have an easy way to enumerate things. For this, you can use a subquery:
select t.letter,
max(case when seqnum = 1 then color end) as color_1,
max(case when seqnum = 2 then color end) as color_2,
max(case when seqnum = 3 then color end) as color_3,
max(case when seqnum = 1 then number end) as number_1,
max(case when seqnum = 2 then number end) as number_2,
max(case when seqnum = 3 then number end) as number_3
from (select t.*,
(select count(*) from t t2 where t2.letter = t.letter and t2.color <= t.color) as seqnum
from t
) t
group by t.letter;
i have a table on oracle 11g that looks like this
col_1 | col_2 | col_3 |
1 | 111222001 | A
2 | 111222001 | B
3 | 111222002 | A
4 | 111222002 | B
5 | 111555001 | B
6 | 111555003 | A
7 | 111555003 | B
i want to order it, to get this
col_1 | col_2 | col_3 |
2 | 111222001 | B
4 | 111222002 | B
1 | 111222001 | A
3 | 111222002 | A
5 | 111555001 | B
7 | 111555003 | B
6 | 111555003 | A
Logic behind it :
notice how col_2 values are values of three triplets 111-222-333.
I want to order col_2 according to the the third triplet 111-222-"333" and get only the entries that have col_3 = 'B' at first, then get those that have col_3 = 'A' .
when the second triplet (changes / goes up) 111-"222"-333 we redo what was described before.
Thanks in advance, i fureg out a way to do it, but it's really ugly, if someone can figure out a way to do it beautifully
Select *
from table
order by col_3 desc, col2, col1;
HI i don't know why i didn't see it, but it was quite simple, sorry for troubling you
select col_1, col_2, col_3 from table_name
order by substr(col_2,3,8), col_3, substr(col_2,9,11);
I have this table:
CREATE TABLE schedule (
schedule_id serial NOT NULL,
start_date date,
CONSTRAINT schedule_id PRIMARY KEY (schedule_element_id)
)
And this table:
CREATE TABLE schedule_user (
schedule_user_id serial NOT NULL,
schedule_id integer,
state int,
CONSTRAINT fk_schedule_id FOREIGN KEY (schedule_id)
REFERENCES schedule (schedule_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
schedule
-------------------------
|schedule_id |date |
|------------+------------|
|1 |'2013-10-10'|
|2 |'2013-10-20'|
|3 |'2013-08-13'|
-------------------------
schedule_user
-----------------------------------
|schedule_user_id|schedule_id |state|
|----------------+------------+-----|
|1 | 1 |0 |
|2 | 1 |1 |
|3 | 1 |2 |
|4 | 1 |0 |
|5 | 1 |1 |
|6 | 1 |1 |
|4 | 2 |0 |
|5 | 2 |1 |
|7 | 2 |0 |
|2 | 3 |1 |
-----------------------------------
And I want a table like this:
characteristic
---------------------------------------
|schedule_id |state0|state1|state2|total|
|------------+------+------+------+-----|
|1 |2 |3 |1 |6 |
|2 |2 |1 |0 |3 |
|3 |1 |1 |0 |2 |
---------------------------------------
I've made this query that looks as as horrible as it's performance.
SELECT
schedule.schedule_id AS id,
(( SELECT count(*) AS count
FROM schedule_user
WHERE schedule_user.schedule_id = schedule.schedule_id
AND state=0))::integer AS state0,
(( SELECT count(*) AS count
FROM schedule_user
WHERE schedule_user.schedule_id = schedule.schedule_id
AND state=1))::integer AS state1,
(( SELECT count(*) AS count
FROM schedule_user
WHERE schedule_user.schedule_id = schedule.schedule_id
AND state=2))::integer AS state2,
(( SELECT count(*) AS count
FROM schedule_user
WHERE schedule_user.schedule_id = schedule.schedule_id))::integer
AS total
FROM schedule
Is there a better way to perform such a query?
Should I create an Index to 'state' column? if so, how should it look like?
You want to make a pivot table. An easy way to make one in SQL if you know all of the possible values of state beforehand is using sum and case statements.
select schedule_id,
sum(case state when 0 then 1 else 0 end) as state0,
sum(case state when 1 then 1 else 0 end) as state1,
sum(case state when 2 then 1 else 0 end) as state2,
count(*) as total
from schedule_user
group by schedule_id;
Another way is to use the crosstab table function.
Neither of these will let you get away with not knowing the set of values of state (and hence the columns in the result set).
I would try
SELECT s.schedule_id,
COUNT(CASE WHEN su.state = 0 THEN 1 END) AS state0,
COUNT(CASE WHEN su.state = 1 THEN 1 END) AS state1,
COUNT(CASE WHEN su.state = 2 THEN 1 END) AS state2,
COUNT(su.state) AS total
FROM schedule s
LEFT
OUTER
JOIN schedule_user su
ON su.schedule_id = s.schedule_id
GROUP
BY s.schedule_id
;
Ths standard approach is to use SUM() with a CASE over a JOIN with a GROUP BY:
SELECT
schedule.schedule_id AS id,
SUM (case when state=0 then 1 else 0 end) AS state0,
SUM (case when state=1 then 1 else 0 end) AS state1,
SUM (case when state=2 then 1 else 0 end) AS state2,
count(*) AS total
FROM schedule
LEFT JOIN schedule_user
ON schedule_user.schedule_id = schedule.schedule_id
GROUP BY 1