How to use query for Oracle pivot? - sql

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

Related

SQL query to find duplicates with mismatching another column

Table looks like this:
col1 | col2
A | B
A | B
D | C
D | C
E | F
E | G
what I need, is to extract col1 E.
Already tried couple variants of SELECT DISTINCT or SELECT ..., COUNT(*) with GROUP BY, but can't figure it out.
p.s. DBMS is Oracle
Such as this?
SQL> with test (col1, col2) as
2 (select 'A', 'B' from dual union all
3 select 'A', 'B' from dual union all
4 select 'D', 'C' from dual union all
5 select 'D', 'C' from dual union all
6 select 'E', 'F' from dual union all
7 select 'E', 'G' from dual
8 )
9 select col1
10 from test
11 group by col1
12 having count(distinct col2) > 1;
C
-
E
SQL>

Select rows when a value appears multiple times

I have a table like this one:
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
+------+------+
I would like to get the IDs that have at least two times A and two times B. So in my example, the query should return only the ID 1,
Thanks!
In MySQL:
SELECT id
FROM test
GROUP BY id
HAVING GROUP_CONCAT(cust ORDER BY cust SEPARATOR '') LIKE '%aa%bb%'
In Oracle
WITH cte AS ( SELECT id, LISTAGG(cust, '') WITHIN GROUP (ORDER BY cust) custs
FROM test
GROUP BY id )
SELECT id
FROM cte
WHERE custs LIKE '%aa%bb%'
I would just use two levels of aggregation:
select id
from (select id, cust, count(*) as cnt
from t
where cust in ('A', 'B')
group by id, cust
) ic
group by id
having count(*) = 2 and -- both customers are in the result set
min(cnt) >= 2 -- and there are at least two instances
This is one option; lines #1 - 13 represent sample data. Query you might be interested in begins at line #14.
SQL> with test (id, cust) as
2 (select 1, 'a' from dual union all
3 select 1, 'a' from dual union all
4 select 1, 'b' from dual union all
5 select 1, 'b' from dual union all
6 select 2, 'a' from dual union all
7 select 2, 'a' from dual union all
8 select 2, 'a' from dual union all
9 select 2, 'b' from dual union all
10 select 3, 'a' from dual union all
11 select 3, 'b' from dual union all
12 select 3, 'b' from dual
13 )
14 select id
15 from (select
16 id,
17 sum(case when cust = 'a' then 1 else 0 end) suma,
18 sum(case when cust = 'b' then 1 else 0 end) sumb
19 from test
20 group by id
21 )
22 where suma = 2
23 and sumb = 2;
ID
----------
1
SQL>
You can use group by and having for the relevant Cust ('A' , 'B')
And query twice (I chose to use with to avoid multiple selects and to cache it)
with more_than_2 as
(
select Id, Cust, count(*) c
from tab
where Cust in ('A', 'B')
group by Id, Cust
having count(*) >= 2
)
select *
from tab
where exists ( select 1 from more_than_2 where more_than_2.Id = tab.Id and more_than_2.Cust = 'A')
and exists ( select 1 from more_than_2 where more_than_2.Id = tab.Id and more_than_2.Cust = 'B')
What you want is a perfect candidate for match_recognize. Here you go:
select id_ as id from t
match_recognize
(
order by id, cust
measures id as id_
pattern (A {2, } B {2, })
define A as cust = 'A',
B as cust = 'B'
)
Output:
Regards,
Ranagal

Oracle Alpha-Numeric column sorting

I am working on to Sort the Revision column of an Oracle DB table in the asc order as per below.
At first the numeric revisions to be sorted (1,2,3,…).
Thereafter Alpha-Numeric to be sorted as following: A, B, B1, C, C1, C2,…,Y, Y2, Y3, Z, AA, AB,..,DA, …ZZ, etc.
Row_Number() in the SELECT statement to be filled with 1,2,3… for each document# (ABC, XYZ) after revision sorting out.
See the uploaded image for the required table.
I tried with SUBSTR , Order by, etc but failed to sort out as per above requirement.
Can someone help me on this ? Thanks!
As I understand your question, you want to put last revisions that contain only two characters and no digits.
You can use a conditional sort:
select
t.*,
row_number() over(
partition by doc#
order by
case when regexp_like(revision, '^\w\d?$') then 0 else 1 end,
revision
) rn
from t
order by doc#, rn
The regular expression describes a string starting with an alphanumeric character, optionally followed by a digit: these revisions should come first.
Demo on DB Fiddle:
with t as (
select 'ABC' doc#, '1' revision from dual
union all select 'ABC', '2' from dual
union all select 'ABC', '3' from dual
union all select 'ABC', 'A' from dual
union all select 'ABC', 'B' from dual
union all select 'ABC', 'B1' from dual
union all select 'ABC', 'C' from dual
union all select 'ABC', 'C1' from dual
union all select 'ABC', 'D' from dual
union all select 'ABC', 'AA' from dual
union all select 'ABC', 'AB' from dual
union all select 'ABC', 'BA' from dual
union all select 'ABC', 'DA' from dual
)
select
t.*,
row_number() over(
partition by doc#
order by
case when regexp_like(revision, '^\w\d?$') then 0 else 1 end,
revision
) rn
from t
order by doc#, rn
DOC# | REVISION | RN
:--- | :------- | -:
ABC | 1 | 1
ABC | 2 | 2
ABC | 3 | 3
ABC | A | 4
ABC | B | 5
ABC | B1 | 6
ABC | C | 7
ABC | C1 | 8
ABC | D | 9
ABC | AA | 10
ABC | AB | 11
ABC | BA | 12
ABC | DA | 13
There is well known old method: rpad(col, max-length, '0')
For example rpad(col, max(length(col)) over(), '0'

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

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

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