T-SQL ORDER BY base on MIN of a group's column - sql

Hi take the following data as an example
id | value
----------
A | 3
A | 9
B | 7
B | 2
C | 4
C | 5
I want to list out all the data base on the min value of each id group, so that the expected output is
id | value
----------
B | 2
B | 7
A | 3
A | 9
C | 4
C | 5
i.e. min of group A is 3, group B is 2, group C is 4, so group B first and then the rest of group B in ascending order. Next group A and then group C
I tried this but thats not what I want
SELECT * FROM (
SELECT 'A' AS id, '3' AS value
UNION SELECT 'A', '9' UNION SELECT 'B', '7' UNION SELECT 'B', '2'
UNION SELECT 'C', '4' UNION SELECT 'C', '5') data
GROUP BY id, value
ORDER BY MIN(value)
Please help! Thank you

SELECT * FROM (
SELECT 'A' AS id, '3' AS value
UNION SELECT 'A', '9' UNION SELECT 'B', '7' UNION SELECT 'B', '2'
UNION SELECT 'C', '4' UNION SELECT 'C', '5') data
ORDER BY MIN(value) OVER(PARTITION BY id), id, value
OVER Clause (Transact-SQL)
Add the over() clause to your query output and you can see what it does for you.
SELECT *,
MIN(value) OVER(PARTITION BY id) OrderedBy FROM (
SELECT 'A' AS id, '3' AS value
UNION SELECT 'A', '9' UNION SELECT 'B', '7' UNION SELECT 'B', '2'
UNION SELECT 'C', '4' UNION SELECT 'C', '5') data
ORDER BY MIN(value) OVER(PARTITION BY id), id, value
Result:
id value OrderedBy
---- ----- ---------
B 2 2
B 7 2
A 3 3
A 9 3
C 4 4
C 5 4

Related

Bigquery - select last # distinct values

Given table as:
WITH table AS
(SELECT 'A' id, '11' ar, 1 ts UNION ALL
SELECT 'A', '12', 2 UNION ALL
SELECT 'A', '11', 3 UNION ALL
SELECT 'B', '11', 4 UNION ALL
SELECT 'B', '13', 5 UNION ALL
SELECT 'B', '12', 6 UNION ALL
SELECT 'B', '12', 7)
id ar ts
A 11 1
A 12 2
A 11 3
B 11 4
B 13 5
B 12 6
B 12 7
I need to get the unique last two rows as:
id ar
A 11
A 12
B 12
B 13
I tried ARRAY_AGG with DISTINCT and LIMIT,
But the ORDER BY must be the same as expression
Below is for BigQuery Standard SQL
#standardSQL
SELECT * EXCEPT(ars)
FROM (
SELECT id, ARRAY_AGG(ar ORDER BY ts DESC LIMIT 2) AS ars
FROM (
SELECT id, ar, MAX(ts) AS ts
FROM `project.dataset.table`
GROUP BY id, ar
)
GROUP BY id
) t, t.ars AS ar
if to apply to sample data from your question - output is
Row id ar
1 A 11
2 A 12
3 B 12
4 B 13

How to use query for Oracle pivot?

I have four columns like below.
Will I be able to get something like this?
Thanks
I would recommend conditional aggregation. It is a database-independent syntax, which is more flexible than Oracle-specific pivot syntax:
select
piece_id,
max(case when attrb_code = 'A' then attrb_a_value end) a,
max(case when attrb_code = 'B' then attrb_a_value end) b,
max(case when attrb_code = 'C' then attrb_a_value end) c,
max(case when attrb_code = 'D' then attrb_b_value end) d
from mytable
group by piece_id
Just use COALESCE (or NVL) in the PIVOT:
SELECT *
FROM table_name
PIVOT (
MAX( COALESCE( attrb_a_value, attrb_b_value ) )
FOR attrb_code IN (
'A' AS A,
'B' AS B,
'C' AS C,
'D' AS D
)
)
So, for your sample data:
CREATE TABLE table_name ( piece_id, attrb_code, attrb_a_value, attrb_b_value ) AS
SELECT 22333, 'A', 8, NULL FROM DUAL UNION ALL
SELECT 22333, 'B', 9, NULL FROM DUAL UNION ALL
SELECT 22333, 'C', 4, NULL FROM DUAL UNION ALL
SELECT 22333, 'D', NULL, 5 FROM DUAL UNION ALL
SELECT 22332, 'A', 2, NULL FROM DUAL UNION ALL
SELECT 22332, 'B', 3, NULL FROM DUAL UNION ALL
SELECT 22332, 'C', 7, NULL FROM DUAL UNION ALL
SELECT 22332, 'D', NULL, 5 FROM DUAL
This outputs:
PIECE_ID | A | B | C | D
-------: | -: | -: | -: | -:
22333 | 8 | 9 | 4 | 5
22332 | 2 | 3 | 7 | 5
db<>fiddle here

Identify only when value matches

I need to return only rows that have the match e.g Value = A, but I only need the rows that have A and with no other values.
T1:
ID Value
1 A
1 B
1 C
2 A
3 A
3 B
4 A
5 B
5 D
5 E
5 F
Desired Output:
2
4
how can I achieve this?
when I try the following, 1&3 are also returned:
select ID from T1 where Value ='A'
With NOT EXISTS:
select t.id
from tablename t
where t.value = 'A'
and not exists (
select 1 from tablename
where id = t.id and value <> 'A'
)
From the sample data you posted there is no need to use:
select distinct t.id
but if you get duplicates then use it.
Another way if there are no null values:
select id
from tablename
group by id
having sum(case when value <> 'A' then 1 else 0 end) = 0
Or if you want the rows where the id has only 1 value = 'A':
select id
from tablename
group by id
having count(*) = 1 and max(value) = 'A'
I think the simplest way is aggregation with having:
select id
from tablename
group by id
having min(value) = max(value) and
min(value) = 'A';
Note that this ignores NULL values so it could return ids with both NULL and A. If you want to avoid that:
select id
from tablename
group by id
having count(value) = count(*) and
min(value) = max(value) and
min(value) = 'A';
Oracle Setup:
CREATE TABLE test_data ( ID, Value ) AS
SELECT 1, 'A' FROM DUAL UNION ALL
SELECT 1, 'B' FROM DUAL UNION ALL
SELECT 1, 'C' FROM DUAL UNION ALL
SELECT 2, 'A' FROM DUAL UNION ALL
SELECT 3, 'A' FROM DUAL UNION ALL
SELECT 3, 'B' FROM DUAL UNION ALL
SELECT 4, 'A' FROM DUAL UNION ALL
SELECT 5, 'B' FROM DUAL UNION ALL
SELECT 5, 'D' FROM DUAL UNION ALL
SELECT 5, 'E' FROM DUAL UNION ALL
SELECT 5, 'F' FROM DUAL
Query:
SELECT ID
FROM test_data
GROUP BY ID
HAVING COUNT( CASE Value WHEN 'A' THEN 1 END ) = 1
AND COUNT( CASE Value WHEN 'A' THEN NULL ELSE 1 END ) = 0
Output:
| ID |
| -: |
| 2 |
| 4 |
db<>fiddle here

Get last value from a certain group (Oracle)

I have something like this
Date Group ID
11/01 'A' 1
12/01 'A' 2
13/01 'B' 3
14/01 'B' 4
What i basically want is to get for example the latest from group 'A'
Date Group ID LatestID_from_GROUP_A_ordered_by_recent_date
11/01 'A' 1 2
12/01 'A' 2 2
13/01 'B' 3 2
14/01 'B' 4 2
or at least something like this
Date Group ID LatestID_from_GROUP_A_ordered_by_recent_date
11/01 'A' 1 null
12/01 'A' 2 null
13/01 'B' 3 2
14/01 'B' 4 2
How about this:
with demo (somedate, somegroup, id) as
( select date '2018-01-11', 'A', 1 from dual union all
select date '2018-01-12', 'A', 2 from dual union all
select date '2018-01-13', 'B', 3 from dual union all
select date '2018-01-14', 'B', 4 from dual union all
select date '2018-01-15', 'A', 5 from dual -- example from comments
)
select somedate, somegroup, id
, ( select max(id) keep (dense_rank last order by somedate)
from demo
where somegroup = 'A' ) as last_a
from demo;
SOMEDATE SOMEGROUP ID LAST_A
----------- --------- ---------- ----------
11/01/2018 A 1 5
12/01/2018 A 2 5
13/01/2018 B 3 5
14/01/2018 B 4 5
15/01/2018 A 5 5
Note the max(id) is only a tiebreaker in the event of multiple rows with the last date.
Gordon was almost there.
You want to create a window over your whole query, but only pick the biggest value of 'A':
select
t.*,
max(case when group = 'A' then id end) over (partition by 1) as latest_from_a
from t
'partition by 1' will create a window of your complete result set because it only groups by a single static value: 1.
The logic seems to be:
select t.*,
max(case when group = 'A' then id end) over (order by date) as latest_from_a
from t;
The above gets the cumulative maximum up to each date. If you want the overall maximum:
select t.*,
max(case when group = 'A' then id end) over () as latest_from_a
from t;

How can I group/aggregate values correlated to 2 distinct values as one?

So in the database there is a value in the 'value_type' column that are interchangeable with each other. When I aggregate, I want to be able to sum / aggregate the two values (A AND B) together but not the other(s) (C)? Some Sample Data is below:
Value_ID Value_Tx Value_Type
1 5 A
2 2 A
3 7 B
4 5 C
5 3 C
6 1 D
7 7 F
The Result I want:
Sum Value_Type
14 A | B
8 C
1 D
7 F
I know that to sum normally I would do:
select sum(value)tx, value_type
from table
group by value_type;
Thanks in advance.
Use case expression with group by.
SELECT SUM(value_tx),
CASE
WHEN value_type IN ( 'A', 'B' ) THEN 'A | B '
ELSE value_type
END
FROM t
GROUP BY CASE
WHEN value_type IN ( 'A', 'B' ) THEN 'A | B '
ELSE value_type
END
ORDER BY 2;
Demo
select sum(value)tx, 'A | B' value_type
from table where value_type in ('A','B')
union all
select sum(value)tx, value_type
from table where value_type not in ('A','B')
group by value_type;
easy way,hope help you : )
I have only tried my query in MySQL
select sum(value_tx),
case
when value_type IN ('A', 'B') THEN 'A | B'
else value_type
end as valtype
from table
group by valtype
Example of how to set the groups dynamically:
WITH test_data AS
(
SELECT 1 val_id, 5 val_tx, 'A' val_typ FROM dual
UNION ALL
SELECT 2, 2, 'A' FROM dual
UNION ALL
SELECT 3, 7, 'B' FROM dual
UNION ALL
SELECT 4, 5, 'C' FROM dual
UNION ALL
SELECT 5, 3, 'C' FROM dual
UNION ALL
SELECT 6, 1, 'D' FROM dual
UNION ALL
SELECT 7, 7, 'F' FROM dual
)
SELECT break_group, SUM(val_tx) total_tx, value_type FROM
(
SELECT val_id, val_tx, val_typ
, CASE WHEN dense_rnk IN (1, 2) THEN 1 ELSE (dense_rnk-1) END break_group
, CASE WHEN dense_rnk IN (1, 2) THEN 'A|B' ELSE val_typ END value_type
FROM
(
SELECT val_id, val_tx, val_typ
, DENSE_RANK() OVER (ORDER BY val_typ) dense_rnk
FROM test_data
)
)
GROUP BY break_group, value_type
ORDER BY break_group
/
Output:
BREAK_GROUP TOTAL_TX VALUE_TYPE
-----------------------------------
1 14 A|B
2 8 C
3 1 D
4 7 F