Oracle join query - sql

There are three tables in my db: ITEM_MASTER, PRICE_MASTER and COMP_MASTER.
ITEM_MASTER
STORE_CODE ITEM_CODE ITEM_DESC
011 914004 desccc
PRICE_MASTER
STORE_CODE ITEM_CODE COMP_CODE
011 914004 01
011 914004 02
011 914004 03
011 914004 04
COMP_MASTER
COMP_CODE COMP_DESC STORE_CODE
01 comp1 011
02 comp2 011
03 comp3 011
04 comp4 011
I want to get all these for an ITEM_CODE in a single query.
STORE_CODE ITEM_CODE ITEM_DESC COMP_DESC1 COMP_DESC2 COMP_DESC3 COMP_DESC4
011 914004 desccc comp1 comp2 comp3 comp4
How can I write an oracle SQL query for this?

There are two steps involved to get this output: a join and a pivot.
An example:
First create your sample tables:
SQL> create table item_master (store_code,item_code,item_desc)
2 as
3 select '011', 914004, 'desccc' from dual
4 /
Table created.
SQL> create table price_master (store_code,item_code,comp_code)
2 as
3 select '011', 914004, '01' from dual union all
4 select '011', 914004, '02' from dual union all
5 select '011', 914004, '03' from dual union all
6 select '011', 914004, '04' from dual
7 /
Table created.
SQL> create table comp_master (comp_code,comp_desc,store_code)
2 as
3 select '01', 'comp1', '011' from dual union all
4 select '02', 'comp2', '011' from dual union all
5 select '03', 'comp3', '011' from dual union all
6 select '04', 'comp4', '011' from dual
7 /
Table created.
First step is the join. Here I use ANSI join syntax, but you can use good old Oracle join syntax as well.
SQL> select i.store_code
2 , i.item_code
3 , i.item_desc
4 , c.comp_desc
5 from item_master i
6 inner join price_master p
7 on ( i.store_code = p.store_code
8 and i.item_code = p.item_code
9 )
10 inner join comp_master c
11 on ( p.store_code = c.store_code
12 and p.comp_code = c.comp_code
13 )
14 /
STO ITEM_CODE ITEM_D COMP_
--- ---------- ------ -----
011 914004 desccc comp1
011 914004 desccc comp2
011 914004 desccc comp3
011 914004 desccc comp4
4 rows selected.
The comp description appear below each other, but you want them to be next to each other. To achieve that, you pivot the result set. Note that you have to hard code the number of rows you want to pivot:
SQL> with t as
2 ( select i.store_code
3 , i.item_code
4 , i.item_desc
5 , c.comp_desc
6 , row_number() over (partition by i.store_code,i.item_code order by c.comp_code) rn
7 from item_master i
8 inner join price_master p
9 on ( i.store_code = p.store_code
10 and i.item_code = p.item_code
11 )
12 inner join comp_master c
13 on ( p.store_code = c.store_code
14 and p.comp_code = c.comp_code
15 )
16 )
17 select store_code
18 , item_code
19 , item_desc
20 , max(decode(rn,1,comp_desc)) comp_desc1
21 , max(decode(rn,2,comp_desc)) comp_desc2
22 , max(decode(rn,3,comp_desc)) comp_desc3
23 , max(decode(rn,4,comp_desc)) comp_desc4
24 from t
25 group by store_code
26 , item_code
27 , item_desc
28 /
STO ITEM_CODE ITEM_D COMP_ COMP_ COMP_ COMP_
--- ---------- ------ ----- ----- ----- -----
011 914004 desccc comp1 comp2 comp3 comp4
1 row selected.
Hope this helps.
Regards,
Rob.

Related

Oracle: fetch value of columns based on another table column

I have two tables, one of them is a main table and the other is the decode. The decode has 142 rows and 2 columns. Cause of death code and description. The main table has person information and the reason of death and secondary reason of death.
Decode table
code
description
1
infection
2
covid
main table
client
reason_1
reason_2
01
1
null
02
2
1
03
1
2
Expected table
client
reason_1
reason_2
reason_1_desc
reason_2_desc
01
1
null
infection
null
02
2
1
covid
infection
03
1
2
infection
covid
How can I get this output without typing in all the values from decode table and using decode function or subquery factoring?
Join the description table twice (for each of two reasons):
Sample data:
SQL> with
2 t_decode (code, description) as
3 (select 1, 'infection' from dual union all
4 select 2, 'covid' from dual
5 ),
6 t_main (client, reason, reason_2) as
7 (select '01', 1, null from dual union all
8 select '02', 2, 1 from dual union all
9 select '03', 1, 2 from dual
10 )
Query:
11 select m.client, m.reason, m.reason_2,
12 d1.description reason_desc,
13 d2.description reason_2_desc
14 from t_main m left join t_decode d1 on d1.code = m.reason
15 left join t_decode d2 on d2.code = m.reason_2
16 order by m.client;
CL REASON REASON_2 REASON_DE REASON_2_
-- ---------- ---------- --------- ---------
01 1 infection
02 2 1 covid infection
03 1 2 infection covid
SQL>

Conditional WHERE clause using a mapping table

I have two tables like below:
1.MAPPING -
ID Mapping ID
A01 P01
A03 P03
2.MAIN -
ID Metric1 Metric2
A01 (null) 4.22
A02 (null) (null)
A03 145 127
A04 (null) 20
P01 68 (null)
P03 (null) (null)
I have to run query for a particular ID. But I have to check two conditions :
If that ID exists in MAPPING table and if one of the Metrics is null
then I have to take Metric value of the Mapping ID.
Suppose for A01 , it exists in MAPPING and Metric1 is null so we have to take the value of Metric1 of P01 i.e. 68. But for Metric2 as it is not null so
we don't have to consider P01.
Now suppose for A02 , it is not in the table MAPPING . So we will
show as it is.
I am using Oracle. Can you please help me how to make an efficient query for this ?
This?
SQL> with
2 mapping (id, mapping_id) as
3 (select 'A01', 'P01' from dual union all
4 select 'A03', 'P03' from dual
5 ),
6 main (id, metric1, metric2) as
7 (select 'A01', null, 4.22 from dual union all
8 select 'A02', null, null from dual union all
9 select 'A03', 145, 127 from dual union all
10 select 'A04', null, 20 from dual union all
11 select 'P01', 68, null from dual union all
12 select 'P03', null, null from dual
13 )
14 select n.id,
15 case when n.metric1 is null then x.metric1 else n.metric1 end metric1,
16 case when n.metric2 is null then x.metric2 else n.metric2 end metric2
17 from main n left join mapping g on n.id = g.id
18 left join main x on x.id = g.mapping_id
19 order by n.id;
ID METRIC1 METRIC2
--- ---------- ----------
A01 68 4,22
A02
A03 145 127
A04 20
P01 68
P03
6 rows selected.
SQL>
Consider query that uses Main twice, joined to each field of Mapping:
SELECT Main.ID, IIf([Main].[Metric1] Is Null,[Main_1].[Metric1],Main.Metric1) AS M1,
IIf([Main].[Metric2] Is Null,[Main_1].[Metric2],Main.Metric2) AS M2
FROM Main AS Main_1
RIGHT JOIN (Main LEFT JOIN Mapping ON Main.ID = Mapping.ID)
ON Main_1.ID = Mapping.MappingID;

Selecting rows with exist operator using OR conditions

I want to select cases from one table, where Code or DST or Short_text or long_text are equal(in 2 or more rows) AND ID are not equal.
ID Code DST Short_text Long_text
1 B 01 B 1 Bez1 Bezirk1
1 B 01 B 1 Bez1 Bezirk1
2 B 02 B 2 Bez2 Bezirk2
3 B 03 B 3 Bez3 Bezirk3
4 B 04 B 4 Bez4 Bezirk4
4 B 04 B 4 Bez4 Bezirk4
5 B 05 B 5 Bez5 Bezirk5
6 B 06 B 6 Bez6 Bezirk6
7 B 07 B 7 Bez7 Bezirk7
8 B 08 B 8 Bez8 Bezirk8
9 B 09 B 9 Bez9 Bezirk9
97 M 51 M 52 MA 51 Sport
96 M 51 M 51 MA 51 Sport
And I want to get the following result:
97 M 51 M 52 MA 51 Sport
96 M 51 M 51 MA 51 Sport
because they have different ID, but they have similar Code OR SImilar Short_text OR simmlar long_text.
Here is what I have tried:
select
ID,
CODE,
DST,
Short_text,
Long_text,
from Main_table tbl
where load_date = (select max(load_date) from Main_table)
and exists
(
select 1 from Main_table
where
tbl.ID != ID
and (tbl.CODE = CODE
or tbl.DST = DST
or tbl.short_text = short_text
or tbl.long_text = long_text)
);
But it doesn't give me a desired result.
Do you have ideas how can I improve my query?
That would be
SQL> select * from main_table;
ID CODE DST SHORT LONG_TE
---------- ---- ---- ----- -------
1 B 01 B 1 Bez1 Bezirk1
1 B 01 B 1 Bez1 Bezirk1
2 B 02 B 2 Bez2 Bezirk2
3 B 03 B 3 Bez3 Bezirk3
4 B 04 B 4 Bez4 Bezirk4
4 B 04 B 4 Bez4 Bezirk4
5 B 05 B 5 Bez5 Bezirk5
6 B 06 B 6 Bez6 Bezirk6
7 B 07 B 7 Bez7 Bezirk7
8 B 08 B 8 Bez8 Bezirk8
9 B 09 B 9 Bez9 Bezirk9
97 M 51 M 52 MA 51 Sport
96 M 51 M 51 MA 51 Sport
13 rows selected.
SQL> select a.*
2 from main_table a
3 join main_table b
4 on a.id <> b.id
5 and ( a.code = b.code
6 or a.dst = b.dst
7 or a.short_text = b.short_text
8 or a.long_text = b.long_text);
ID CODE DST SHORT LONG_TE
---------- ---- ---- ----- -------
97 M 51 M 52 MA 51 Sport
96 M 51 M 51 MA 51 Sport
SQL>
You can use analytic functions to avoid a self-join:
Oracle Setup:
CREATE TABLE table_name ( ID, Code, DST, Short_text, Long_text ) as
select 1, 'B 01', 'B 1', 'Bez1', 'Bezirk1' from dual union all
select 1, 'B 01', 'B 1', 'Bez1', 'Bezirk1' from dual union all
select 2, 'B 02', 'B 2', 'Bez2', 'Bezirk2' from dual union all
select 3, 'B 03', 'B 3', 'Bez3', 'Bezirk3' from dual union all
select 4, 'B 04', 'B 4', 'Bez4', 'Bezirk4' from dual union all
select 4, 'B 04', 'B 4', 'Bez4', 'Bezirk4' from dual union all
select 5, 'B 05', 'B 5', 'Bez5', 'Bezirk5' from dual union all
select 6, 'B 06', 'B 6', 'Bez6', 'Bezirk6' from dual union all
select 7, 'B 07', 'B 7', 'Bez7', 'Bezirk7' from dual union all
select 8, 'B 08', 'B 8', 'Bez8', 'Bezirk8' from dual union all
select 9, 'B 09', 'B 9', 'Bez9', 'Bezirk9' from dual union all
select 97, 'M 51', 'M 52', 'MA 51', 'Sport' from dual union all
select 96, 'M 51', 'M 52', 'MA 51', 'Sport' from dual;
Query:
SELECT ID, Code, DST, Short_text, Long_text
FROM (
SELECT t.*,
COUNT( DISTINCT id ) OVER ( PARTITION BY code ) AS num_code,
COUNT( DISTINCT id ) OVER ( PARTITION BY dst ) AS num_dst,
COUNT( DISTINCT id ) OVER ( PARTITION BY short_text ) AS num_short_text,
COUNT( DISTINCT id ) OVER ( PARTITION BY long_text ) AS num_long_text
FROM table_name t
)
WHERE num_code > 1
OR num_dst > 1
OR num_short_text > 1
OR num_long_text > 1
Output:
ID | CODE | DST | SHORT_TEXT | LONG_TEXT
-: | :--- | :--- | :--------- | :--------
96 | M 51 | M 52 | MA 51 | Sport
97 | M 51 | M 52 | MA 51 | Sport
db<>fiddle here
You can use count(*) aggregation containing having clauses consecutively :
select ID, Code, DST, Short_text, Long_text
from Main_table
where (Code, DST, Short_text, Long_text) in
(select Code, DST, Short_text, Long_text
from Main_table
group by Code, DST, Short_text, Long_text
having count(*) > 1)
group by ID, Code, DST, Short_text, Long_text
having count(*) = 1
or count(*) over (partition by...) analytic function to be considered including partition clauses with and without containing ID column :
with m2 as
(
select m.*,
count(*) over ( partition by Code, DST, Short_text, Long_text ) as cnt1,
count(*) over ( partition by ID, Code, DST, Short_text, Long_text ) as cnt2
from Main_table m
)
select ID, Code, DST, Short_text, Long_text
from m2
where cnt1 > 1 and cnt2 = 1
Demo
You can use below query
select mt1.ID, mt1.Code, mt1.DST, mt1.Short_text, mt1.Long_text from main_table as mt1
Cross Apply(
select * from main_table as mt2 where mt1.id!= mt2.id and ( mt1.code=mt2.code or mt1.short_text =mt2.short_text or mt1.long_text = mt2.long_text )
) cv

find the difference between 2 rows

I need to find the difference between the rows c2 in the below table
SEQ_ID Priv_ID Common_ID Source_ID C1 C2
------ -------- --------- --------- -- ---
1 1 C001 S1 abc 32331299300
2 1 C001 S1 def 12656678121
3 1 C001 S1 ghi 8966492700
4 1 C001 S2 abc 32331292233
5 1 C001 S2 ghi 8966492700
6 1 C001 S2 def 12656672000
expected output should be as below,
SEQ_ID Priv_ID Common_ID C1 C2
------ -------- --------- -- ---
1 1 C001 abc 7067
2 1 C001 def 6121
3 1 C001 ghi 0
Please assist.
How about this? I didn't use columns that are the same for all rows (so they don't make any difference).
SQL> with test (seq_id, source_id, c1, c2) as
2 (select 1, 's1', 'abc', 32331299300 from dual union all
3 select 2, 's1', 'def', 12656678121 from dual union all
4 select 3, 's1', 'ghi', 8966492700 from dual union all
5 select 4, 's2', 'abc', 32331292233 from dual union all
6 select 5, 's2', 'ghi', 8966492700 from dual union all
7 select 6, 's2', 'def', 12656672000 from dual
8 )
9 select min(seq_id) seq_id,
10 c1,
11 max(case when source_id = 's1' then c2 end) +
12 max(case when source_id = 's2' then -c2 end) c2
13 from test
14 group by c1
15 order by 1;
SEQ_ID C1 C2
---------- --- ----------
1 abc 7067
2 def 6121
3 ghi 0
SQL>
Hmmm . . . One method would be conditional aggregation. But the key is row_number():
select Priv_ID, Common_ID, c1,
max(case when source_ID = 'S1' then c2
when source_ID = 'S2' then -c2
end) as diff
from (select t.*,
row_number() over (partition by Priv_ID, Common_ID, c1 order by seq_id) as seqnum
from t
) t
group by Priv_ID, Common_ID, c1

Group by or concatenate multiple columns by multiple criteria

I need to do what I thought was going to be a simple SQL query.. but I got stuck deciding how can these be grouped:
<p>I've got the below table:</p>
Company | Airport | Type
------------------------------------------
SP1 | AP1 | ST1
SP1 | AP1 | ST2
SP1 | AP1 | ST3
SP1 | AP2 | ST1
SP1 | AP2 | ST2
SP1 | AP2 | ST3
SP1 | AP3 | ST1
SP1 | AP3 | ST2
SP1 | AP4 | ST1
SP1 | AP4 | ST2
SP1 | AP4 | ST3
SP1 | AP4 | ST4
I want to group AP and ST in the following way so that the desired result is like this:
(CASE 1)
SP | AP | ST
------------------------------------------
SP1 | AP1, AP2, AP4 | ST1, ST2, ST3
SP1 | AP3 | ST1, ST2
SP1 | AP4 | ST4
Any thoughts? Really appreciated!
Update
As pointed out, there is another alternative for the result:
(CASE 2)
SP | AP | ST
------------------------------------------
SP1 | AP1, AP2 | ST1, ST2, ST3
SP1 | AP3 | ST1, ST2
SP1 | AP4 | ST1, ST2, ST3, ST4
I've also added titles to the columns to give a bit more context. The idea is just that, to be able to group associated elements. I'm happy with any of the two results, hopefully both alternatives if possible..
It's not described why AP4/ST4 is a special case, but supposing you try to group withing 3 sequential elements in ST for each (sp,ap):
SQL> with t (SP, AP, ST) as (
2 select 'SP1','AP1','ST1' from dual union all
3 select 'SP1','AP1','ST2' from dual union all
4 select 'SP1','AP1','ST3' from dual union all
5 select 'SP1','AP2','ST1' from dual union all
6 select 'SP1','AP2','ST2' from dual union all
7 select 'SP1','AP2','ST3' from dual union all
8 select 'SP1','AP3','ST1' from dual union all
9 select 'SP1','AP3','ST2' from dual union all
10 select 'SP1','AP4','ST1' from dual union all
11 select 'SP1','AP4','ST2' from dual union all
12 select 'SP1','AP4','ST3' from dual union all
13 select 'SP1','AP4','ST4' from dual
14 )
15 select sp, listagg(ap,',') within group (order by ap) lstap, lstst
16 from (
17 select sp, ap, listagg(st,',') within group (order by st) lstst from (
18 select sp, ap, st, ceil((row_number() over(partition by sp, ap order by st))/3) grp
19 from t
20 )
21 group by sp, ap, grp
22 )
23 group by sp, lstst
24 order by 1,2,3
25 /
SP LSTAP LSTST
--- ------------------------- -------------------------
SP1 AP1,AP2,AP4 ST1,ST2,ST3
SP1 AP3 ST1,ST2
SP1 AP4 ST4
P.S. For alternative result output:
SQL> with t (SP, AP, ST) as (
2 select 'SP1','AP1','ST1' from dual union all
3 select 'SP1','AP1','ST2' from dual union all
4 select 'SP1','AP1','ST3' from dual union all
5 select 'SP1','AP2','ST1' from dual union all
6 select 'SP1','AP2','ST2' from dual union all
7 select 'SP1','AP2','ST3' from dual union all
8 select 'SP1','AP3','ST1' from dual union all
9 select 'SP1','AP3','ST2' from dual union all
10 select 'SP1','AP4','ST1' from dual union all
11 select 'SP1','AP4','ST2' from dual union all
12 select 'SP1','AP4','ST3' from dual union all
13 select 'SP1','AP4','ST4' from dual
14 )
15 select sp, listagg(ap,',') within group (order by ap) lstap, lstst
16 from (
17 select sp, ap, listagg(st,',') within group (order by st) lstst from (
18 select sp, ap, st
19 from t
20 )
21 group by sp, ap
22 )
23 group by sp, lstst
24 order by 1,2,3
25 /
SP LSTAP LSTST
--- ------------------------- -------------------------
SP1 AP1,AP2 ST1,ST2,ST3
SP1 AP3 ST1,ST2
SP1 AP4 ST1,ST2,ST3,ST4