Custom Order for Max() - sql

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;

Related

With some WHERE clause criteria, have SQL output go to next line

I am trying to write a query where I have some criteria where I pivot the results. However, due to output file constraints I am looking for the output to create a new line after the pivot exceeds X, even if the ID and such is otherwise the same.
What I am trying to do:
|--ID--|-Value-|
| 1 | val1 |
| 1 | val2 |
| 1 | val3 |
| 2 | val1 |
|--ID--|-Col1-|-Col2-|
| 1 | Val1| Val2|
| 1 | Val3| |
| 2 | Val1| |
SELECT *
FROM table
PIVOT(max(value) for field1 in (t1,t2)
as pvt
ORDER BY UNIQUE_ID
This is just a pivot example to pivot this particular column. However the output has a very strict number of column requirement so I'd be looking for any pivot beyond the 5th to "overflow" to the next row while retaining the unique id. I am looking at PIVOT but I dont think it will work here.
Is this even possible within the Snowflake platform or do I need to explore other options?
This requirement is purely presentation matter and in my opinion should not be performed at the database level. With that being said it is possible to achieve it by numbering rows in group and performing modulo division:
Samle data:
CREATE OR REPLACE TABLE tab
AS
SELECT 1 AS id, 'val1' AS value UNION
SELECT 1 AS id, 'val2' AS value UNION
SELECT 1 AS id, 'val3' AS value UNION
SELECT 2 AS id, 'val1' AS value;
Query:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT
id
,MAX(CASE WHEN rn % 2 = 0 THEN value END) AS col1
,MAX(CASE WHEN rn % 2 = 1 THEN value END) AS col2
FROM cte
GROUP BY id, FLOOR(rn / 2)
ORDER BY id, FLOOR(rn / 2);
Intermediate result:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT id,value, rn, FLOOR(rn / 2) AS row_index, rn % 2 AS column_index
FROM cte
ORDER BY ID, rn;
Generalized:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT
id
,MAX(CASE WHEN rn % N = 0 THEN value END) AS col1
,MAX(CASE WHEN rn % N = 1 THEN value END) AS col2
-- ....
,MAX(CASE WHEN rn % N = N-1 THEN value END) AS colN
FROM cte
GROUP BY id, FLOOR(rn / N)
ORDER BY id, FLOOR(rn / N);

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;

increment if not same value of next column in SQL

I am trying to use the Row Number in SQL. However, it's not giving desired output.
Data :
ID Name Output should be
111 A 1
111 B 2
111 C 3
111 C 3
111 A 4
222 A 1
222 A 1
222 B 2
222 C 3
222 B 4
222 B 4
This is a gaps-and-islands problem. As a starter: for the question to just make sense, you need a column that defines the ordering of the rows - I assumed ordering_id. Then, I would recommend lag() to get the "previous" name, and a cumulative sum() that increases everytime the name changes in adjacent rows:
select id, name,
sum(case when name = lag_name then 0 else 1 end) over(partition by id order by ordering_id) as rn
from (
select t.*, lag(name) over(partition by id order by ordering_id) lag_name
from mytable t
) t
SQL Server 2008 makes this much trickier. You can identify the adjacent rows using a difference of rows numbers. Then you can assign the minimum id in each island and use dense_rank():
select t.*,
dense_rank() over (partition by name order by min_ordcol) as output
from (select t.*,
min(<ordcol>) over (partition by name, seqnum - seqnum_2) as min_ordcol
from (select t.*,
row_number() over (partition by name order by <ordcol>) as seqnum,
row_number() over (partition by name, id order by <ordcol>) as seqnum_2
from t
) t
) t;

Easy table transpose in hive

I need to transpose my table.
Now i have that type of table:
Atr_1|Atr_2
A | 1
A | 2
But i want to get the next result
Atr_1|Atr_2|Atr_3
A | 1 | 2
How should i transpose my table for achieving this result?
Use case statements with min() or max() aggregation:
select Atr_1,
max(case when Atr_2=1 then 1 end ) Attr_2,
max(case when Atr_2=2 then 2 end ) Attr_3
from table t
group by Atr_1;
If you have only two values, min() and max() do what you want:
select atr_1, min(atr_2) as atr_2, max(atr_3) as atr_3
from t
group by atr_1;
I think you want aggregation :
SELECT atr_1,
MAX(CASE WHEN SEQ = 1 THEN atr_2 END) as atr_2,
MAX(CASE WHEN SEQ = 2 THEN atr_2 END) as atr_3
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY atr_1 ORDER BY atr_2) AS SEQ
FROM table t
) t
GROUP BY atr_1;

Pivot data in SQL (repeated levels)

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