Pivot data in SQL (repeated levels) - sql

I have a question regarding pivoting data in SQL.
Input data:
TABLE NAME temp
id cat value
1 A 22
1 B 33
1 C 44
1 C 55
My ideal output would be:
id A B C
1 22 33 44
1 22 33 55
Can someone provide some hints on this?
Thanks!

select * from
(
select
id,cat,value
from tablename
)
as tablo
pivot
(
sum(value)
for cat in ([A],[B],[C])
) as p
order by id

use case when, assuming you did a mistake in output format in 2nd rows
select id, max( case when cat='A' then value end) as A,
max(case when cat='B' then value end) as B,
max(case when cat='C' then value end)as C from table
group by id

You need row_number() function with conditional aggregation :
select id, max(case when cat = 'a' then value end) a,
max(case when cat = 'b' then value end) b,
max(case when cat = 'c' then value end) c
from (select t.*, row_number() over (partition by id, cat order by value) as seq
from table t
) t
group by id, seq;
However, it doesn't produce your actual output (it leaves null value where the cat has only one value compare to other cats) but it will give the idea of how to do that.

Use CASE WHEN and MAX aggregation:
select id, max(case when cat='A' then value end) as A,max(case when cat='B' then value end) as B,
max(case when cat='C' then value end) as C from temp
group by id

Related

Keep multiple rows during PIVOT in Snowflake

I would like to transpose rows into columns in Snowflake.
Suppose I have the following table BASE
ID
value
type
1
100
'A'
1
200
'B'
1
300
'B'
2
400
'A'
The output should be as follows:
ID
A
B
1
100
200
1
100
300
2
400
NULL
Currently I am pivoting the table with
SELECT ID,
CASE WHEN TYPE = 'A' THEN VALUE ELSE NULL AS A,
CASE WHEN TYPE = 'B' THEN VALUE ELSE NULL AS B
FROM BASE
For now the GROUP BY statement is missing. Typically I would GROUP BY ID, but that does not account for keeping one row per each value on the same TYPE and ID.
Any ideas how to achieve this?
Cheers,
P
You can use conditional aggregation. You can use row_number() to get multiple rows:
SELECT ID,
MAX(CASE WHEN TYPE = 'A' THEN VALUE END) AS A,
MAX(CASE WHEN TYPE = 'B' THEN VALUE END) AS B
FROM (SELECT B.*,
ROW_NUMBER() OVER (PARTITION BY ID, TYPE ORDER BY VALUE) as seqnum
FROM BASE B
) B
GROUP BY ID, seqnum;
This would work, too:
select *
from base_table
pivot(sum(value) for type in ('A','B')) as p
order by id;

Is it possible to add column name from the other's column value

Given table:
ID NAME VALUE
1 A N
1 B Y
1 C N
I want the table in below format:
ID A B C
1 N Y N
You can use conditional aggregation
select id,
max(case when name='A' then value end) as A,
max(case when name='B' then value end) as B,
max(case when name='C' then value end) as C
from tablename
group by id

selecting the highest count for a categorical variable when grouping

I have the following table:
custID Cat
1 A
1 B
1 B
1 B
1 C
2 A
2 A
2 C
3 B
3 C
4 A
4 C
4 C
4 C
What I need is the most efficient way to aggregate by CustID in such a manner that I obtain the most frequent category (cat), the second most frequent and the third. The output of the above should be
most freq 2nd most freq 3rd most freq
1 B A C
2 A C Null
3 B C Null
4 C A Null
When there is a tie in the count I do not really care what is first and what is second. For example for customer 1 2nd most freq and 3rd most freq could be swapped because each of them occur 1 time only.
Any sql would be fine, preferable hive sql.
Thank you
Try to use group by twice and dense_rank() to sort accorting to the cat count. Actually I'm not 100% sure , but I guess it should work in hive as well.
select custId,
max(case when t.rn = 1 then cat end) as [most freq],
max(case when t.rn = 2 then cat end) as [2nd most freq],
max(case when t.rn = 3 then cat end) as [3th most freq]
from
(
select custId, cat, dense_rank() over (partition by custId order by count(*) desc) rn
from your_table
group by custId, cat
) t
group by custId
demo
According to the comments I add slightly modified solution that conforms with Hive SQL
select custId,
max(case when t.rn = 1 then cat else null end) as most_freq,
max(case when t.rn = 2 then cat else null end) as 2nd_most_freq,
max(case when t.rn = 3 then cat else null end) as 3th_most_freq
from
(
select custId, cat, dense_rank() over (partition by custId order by ct desc) rn
from (
select custId, cat, count(*) ct
from your_table
group by custId, cat
) your_table_with_counts
) t
group by custId
Hive SQL demo
SELECT journal, count(*) as frequency
FROM ${hiveconf:TNHIVE}
WHERE journal IS NOT NULL
GROUP BY journal
ORDER BY frequency DESC
LIMIT 5;

Custom Order for Max()

I want to get the "max" character value for a column using a group by statement, except instead of the default alphabetical order, I want to set up a custom ordering that the max will use.
Table1:
ID | TYPE
-----+-------
1 | A
1 | B
1 | C
2 | A
2 | B
I want to group by ID and get max(type) in the order of C, A, B. Expected result:
ID | MAX_TYPE
-----+-----------
1 | C
2 | A
select
id,
case
max(
case max_type
when 'C' then 3 when 'A' then 2 when 'B' then 1
end
)
when 3 then 'C' when 2 then 'A' when 1 then 'B'
end as max_type
from T
group by id
Translate to a value that an be ranked by max() and then translate back to the original value.
If you also want to order the result by that value then you could add:
order by
max(
case max_type
when 'C' then 3 when 'A' then 2 when 'B' then 1
end
) desc
Some platforms require the sorting column to be included in the output. I'm not sure if PostgreSql is one of those. And no objection to Gordon's answer but you'd have to use another window function to calculate the sort order if you need that too.
Instead of translating back and forth, use window functions:
select t.*
from (select t.*,
row_number() over (partition by id
order by (case when type = 'C' then 1
when type = 'A' then 2
when type = 'B' then 3
end) as seqnum
from t
) t
where seqnum = 1;
Depending on what the values look like, you can also simplify this using string functions:
select t.*
from (select t.*,
row_number() over (partition by id
order by position(type, 'CAB')) as seqnum
from t
) t
where seqnum = 1;

Oracle query group by type

I have an output from my query:
Item Type Qty
1 A 2
2 A 3
3 B 1
4 B 2
5 C 1
6 D 3
Type to be grouped: A, B
I need my output to look like this:(after sum the qty and group by)
Type Qty
A 5
B 3
OTHERS 4
when the Type is not defined, it will group in 'OTHERS'. Is this possible to be done using analytic function or do I need to create my own function for this?
Assuming your table / view name is x, this gives exactly the desired output:
SELECT CASE WHEN type IN ('A', 'B') THEN type ELSE 'OTHERS' END AS type,
SUM(qty) AS qty
FROM x
GROUP BY CASE WHEN type IN ('A', 'B') THEN type ELSE 'OTHERS' END
ORDER BY 1
select type,sum(qty) from (select decode(type,c,'OTHERS',d,'OTHERS') type,qty from your_table) group by type;
(or)
select type,sum(qty) from (select (case when type in ('A','B') then type else 'others' end) type,qty from your_table) group by type;