Related
I have two tables PLACE and ADRESS. PLACE includes places like parent-child hierarcy. ADRESS includes adresses with PLACE_ID column. These tables as shown below;
PLACE
ID PARENT_ID NAME CONTINENT
11 null USA America
22 11 New York America
33 22 Manhattan America
44 null Brasil America
55 44 Rio America
66 null France Europe
77 66 Paris Europe
88 66 Nice Europe
MEMBER
ID PLACE_ID NAME ADRESS
1 22 .. ..
2 77 .. ..
3 33 .. ..
4 22 .. ..
5 55 .. ..
6 55 .. ..
7 88 .. ..
8 88 .. ..
9 88 .. ..
10 22 .. ..
Expected Result:
ID PARENT_ID MEMBER_COUNT PLACE_NAME CONTITNET
11 null 4 USA America
22 11 4 New York America
33 22 1 Manhattan America
44 null 2 Brasil America
55 44 2 Rio America
66 null 4 France Europe
77 66 1 Paris Europe
88 66 3 Nice Europe
I want to find out how many members are in which place. I couldn't add child places's member count to parent place. My query as shown below;
WITH MEMBER_COUNT_BY_PLACE AS (
SELECT P.PLACE_ID, COUNT(P.ID) AS MEMBER_COUNT
FROM MEMBER P
GROUP BY P.PLACE_ID
) SELECT C.ID, C.NAME, C.PARENT_ID AS PARENTID, C.CONTINENT, SUM(NVL(D.MEMBER_COUNT, 0)) AS MEMBER_COUNT
FROM PLACE C LEFT JOIN MEMBER_COUNT_BY_PLACE D ON D.PLACE_ID = C.ID
START WITH D.PLACE_ID IS NOT NULL
CONNECT BY PRIOR C.PARENT_ID = C.ID
GROUP BY C.ID, C.NAME, C.PARENT_ID, C.CONTINENT
ORDER BY CONTINENT ASC, PARENT_ID ASC NULLS FIRST;
Thank you for your help.
Here's one option:
Sample data first:
SQL> with
2 -- sample data
3 place (id, parent_id, name, continent) as
4 (select 11, null, 'USA' , 'America' from dual union all
5 select 22, 11 , 'New York' , 'America' from dual union all
6 select 33, 22 , 'Manhattan', 'America' from dual union all
7 select 44, null, 'Brasil' , 'America' from dual union all
8 select 55, 44 , 'Rio' , 'America' from dual union all
9 select 66, null, 'France' , 'Europe' from dual union all
10 select 77, 66 , 'Paris' , 'Europe' from dual union all
11 select 88, 66 , 'Nice' , 'Europe' from dual
12 ),
13 member (id, place_id) as
14 (select 1, 22 from dual union all
15 select 2, 77 from dual union all
16 select 3, 33 from dual union all
17 select 4, 22 from dual union all
18 select 5, 55 from dual union all
19 select 6, 55 from dual union all
20 select 7, 88 from dual union all
21 select 8, 88 from dual union all
22 select 9, 88 from dual union all
23 select 10, 22 from dual
24 ),
Then, a few CTEs (see comments):
25 -- naively, count members of leaf nodes
26 naive as
27 (select m.place_id, count(*) cnt
28 from member m
29 group by m.place_id
30 ),
31 -- set root parent node to each row
32 cbr as
33 (select connect_by_root p.id as tpid,
34 p.id, p.parent_id, n.cnt
35 from place p left join naive n on p.id = n.place_id
36 connect by prior p.id = p.parent_id
37 start with p.parent_id is null
38 order by p.id
39 ),
40 -- how many members does each root node have?
41 sumtpid as
42 (select c.tpid, sum(c.cnt) cnt
43 from cbr c
44 group by c.tpid
45 )
46 -- join CBR + SUMTPID + PLACE for the final result
47 select c.id, c.parent_id, nvl(c.cnt, s.cnt) member_count,
48 p.name place_name,
49 p.continent
50 from cbr c join sumtpid s on s.tpid = c.tpid
51 join place p on p.id = c.id
52 order by c.id;
Which results in:
ID PARENT_ID MEMBER_COUNT PLACE_NAM CONTINE
---------- ---------- ------------ --------- -------
11 4 USA America
22 11 3 New York America
33 22 1 Manhattan America
44 2 Brasil America
55 44 2 Rio America
66 4 France Europe
77 66 1 Paris Europe
88 66 3 Nice Europe
8 rows selected.
SQL>
This query also aggregates child members:
with mcnt (id, parent_id, name, continent, child_members, own_members, ff,level_members) as (
select
p.id, p.parent_id, p.name, p.continent
, 0 as child_members
, (select count(*) from member m where m.place_id=p.id) as own_members
, row_number()over(partition by p.parent_id order by p.id) ff
, sum((select count(*) from member m where m.place_id=p.id))over(partition by p.parent_id) level_members
from place p
where not exists(select null from place child where p.id = child.parent_id)
union all
select p.id, p.parent_id, p.name, p.continent
, mcnt.level_members + mcnt.child_members as child_members
, (select count(*) from member m where m.place_id=p.id) as own_members
, row_number()over(partition by p.parent_id order by p.id) ff
, sum((select count(*) from member m where m.place_id=p.id))over(partition by p.parent_id) level_members
from mcnt, place p
where mcnt.parent_id=p.id
and mcnt.ff=1
)
select
id, parent_id, name, continent, child_members, own_members
,child_members+own_members as total_cnt
from mcnt
order by id;
Full example:
with
-- sample data
place (id, parent_id, name, continent) as
(select 11, null, 'USA' , 'America' from dual union all
select 22, 11 , 'New York' , 'America' from dual union all
select 33, 22 , 'Manhattan', 'America' from dual union all
--
select 35, 33 , 'Central Park', 'America' from dual union all
select 44, null, 'Brasil' , 'America' from dual union all
select 55, 44 , 'Rio' , 'America' from dual union all
select 66, null, 'France' , 'Europe' from dual union all
select 77, 66 , 'Paris' , 'Europe' from dual union all
select 88, 66 , 'Nice' , 'Europe' from dual
),
member (id, place_id) as
(select 1, 22 from dual union all
select 2, 77 from dual union all
select 3, 33 from dual union all
select 4, 22 from dual union all
select 5, 55 from dual union all
select 6, 55 from dual union all
select 7, 88 from dual union all
select 8, 88 from dual union all
select 9, 88 from dual union all
select 10, 22 from dual
--
union all select 1001, 35 from dual
union all select 1002, 35 from dual
union all select 1003, 35 from dual
),
mcnt (id, parent_id, name, continent, child_members, own_members, ff,level_members) as (
select
p.id, p.parent_id, p.name, p.continent
, 0 as child_members
, (select count(*) from member m where m.place_id=p.id) as own_members
, row_number()over(partition by p.parent_id order by p.id) ff
, sum((select count(*) from member m where m.place_id=p.id))over(partition by p.parent_id) level_members
from place p
where not exists(select null from place child where p.id = child.parent_id)
union all
select p.id, p.parent_id, p.name, p.continent
, mcnt.level_members + mcnt.child_members as child_members
, (select count(*) from member m where m.place_id=p.id) as own_members
, row_number()over(partition by p.parent_id order by p.id) ff
, sum((select count(*) from member m where m.place_id=p.id))over(partition by p.parent_id) level_members
from mcnt, place p
where mcnt.parent_id=p.id
and mcnt.ff=1
)
select
id, parent_id, name, continent, child_members, own_members
,child_members+own_members as total_cnt
from mcnt
order by id;
Results:
ID PARENT_ID NAME CONTINE CHILD_MEMBERS OWN_MEMBERS TOTAL_CNT
---------- ---------- ------------ ------- ------------- ----------- ----------
11 USA America 7 0 7
22 11 New York America 4 3 7
33 22 Manhattan America 3 1 4
35 33 Central Park America 0 3 3
44 Brasil America 2 0 2
55 44 Rio America 0 2 2
66 France Europe 4 0 4
77 66 Paris Europe 0 1 1
88 66 Nice Europe 0 3 3
9 rows selected.
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
We have tables Table1 and Table2
Table 1
ID name subject
1 xxx physics
maths
chemistry
2 yyy physics
maths
chemistry
Table 2
Id Marks
1 70
67
88
2 90
99
89
We need to join the above two tables like this format with condition Table1.Id=Table2.Id,
Id name subject Marks
1 xxx physics 70
maths 67
chemistry 88
2 yyy physics 90
maths 99
chemistry 89
#standardSQL
WITH `project.dataset.table1` AS (
SELECT 1 id, 'xxx' name, ['physics', 'maths', 'chemistry'] subject UNION ALL
SELECT 2, 'yyy', ['physics', 'maths', 'chemistry']
), `project.dataset.table2` AS (
SELECT 1 id, [70, 67, 88] marks UNION ALL
SELECT 2, [90, 99, 89]
)
SELECT *
FROM `project.dataset.table1` t1
JOIN `project.dataset.table2` t2
USING(id)
with result
Row id name subject marks
1 1 xxx physics 70
maths 67
chemistry 88
2 2 yyy physics 90
maths 99
chemistry 89
enter image description hereI have two tables
Table 1-qualified players
Id_sequence
121
345
765
Table 2 - All enteries related to players Note :there are only 6 game_ids 1a,1b,1c,2a,2b,2c
id_sequence player_id game_id phone_no
121 aaa 1a 111111111
131 aaa 1b 111222111
141 aaa 1a 111112222
345 bbb 2a 222222222
656 bbb 2c 222211122
789 bbb 1c 222211122
632 bbb 2b 222222222
765 ccc 2b 333333333
897 ddd 1a 444444444
433 ddd 2c 555555444
Expected Output:
select all id_sequence from Table 1 and join Table 2.
Then select all other data related to that player_id then aggregate and consolidate the counts
player_id game id_1a game id_1b game id_1c game id_2a game id_2b game id_2c no_of _phones
aaa 2 1 3
bbb 1 1 1 1 2
ccc 1 1
Currently,I am creating a table 3 that stores the player_id obtained from joining table 1 and table 2 .Then again joining table 3 and 2.
Any thoughts and ideas will greatly help.
Test data (see also: dbfiddle)
SQL> select * from ae;
ID_SEQUENCE PLAYER_ID GAME_ID PHONE_NO
121 aaa 1a 111111111
131 aaa 1b 111222111
141 aaa 1a 111112222
345 bbb 2a 222222222
656 bbb 2c 222211122
789 bbb 1c 222211122
632 bbb 2b 222222222
765 ccc 2b 333333333
897 ddd 1a 444444444
433 ddd 2c 555555444
Query
select
dt1.*
, dt2.no_of_phones
from (
(
select game_id, player_id
from ae
) pivot
(
count( game_id ) for game_id in
(
'1a' as "game id_1a"
, '1b' as "game id_1b"
, '1c' as "game id_1c"
, '2a' as "game id_2a"
, '2b' as "game id_2b"
, '2c' as "game id_2c"
)
)
) dt1 join (
select
player_id
, count( phone_no ) no_of_phones
from ae
group by player_id
) dt2
on dt1.player_id = dt2.player_id
order by dt1.player_id
;
Result
PLAYER_ID game id_1a game id_1b game id_1c game id_2a game id_2b game id_2c NO_OF_PHONES
aaa 2 1 0 0 0 0 3
bbb 0 0 1 1 1 1 4
ccc 0 0 0 0 1 0 1
ddd 1 0 0 0 0 1 2
A PIVOT should take care of it
SQL> create table t1 ( id int);
Table created.
SQL>
SQL> insert into t1 values (121 );
1 row created.
SQL> insert into t1 values (345 );
1 row created.
SQL> insert into t1 values (765 );
1 row created.
SQL>
SQL>
SQL> create table t2 ( id int, player varchar2(10), game varchar2(10), phone int );
Table created.
SQL>
SQL> insert into t2 values (121 ,'aaa' , '1a' , 111111111);
1 row created.
SQL> insert into t2 values (131 ,'aaa' , '1b' , 111222111 );
1 row created.
SQL> insert into t2 values (141 ,'aaa' , '1a' , 111112222 );
1 row created.
SQL> insert into t2 values (345 ,'bbb' , '2a' , 222222222 );
1 row created.
SQL> insert into t2 values (656 ,'bbb' , '2c' , 222211122 );
1 row created.
SQL> insert into t2 values (789 ,'bbb' , '1c' , 222211122 );
1 row created.
SQL> insert into t2 values (632 ,'bbb' , '2b' , 222222222 );
1 row created.
SQL> insert into t2 values (765 ,'ccc' , '2b' , 333333333 );
1 row created.
SQL> insert into t2 values (897 ,'ddd' , '1a' , 444444444 );
1 row created.
SQL> insert into t2 values (433 ,'ddd' , '2c' , 555555444);
1 row created.
SQL>
SQL>
SQL> SELECT *
2 FROM (SELECT player, game, phone
3 FROM t2)
4 PIVOT (count(phone) AS cnt FOR (game) IN ('1a','1b','2a','2b','3a','3c'));
PLAYER '1a'_CNT '1b'_CNT '2a'_CNT '2b'_CNT '3a'_CNT '3c'_CNT
---------- ---------- ---------- ---------- ---------- ---------- ----------
aaa 2 1 0 0 0 0
bbb 0 0 1 1 0 0
ddd 1 0 0 0 0 0
ccc 0 0 0 1 0 0
4 rows selected.
Addenda to get NO_PHONES
SQL> SELECT *
2 FROM (SELECT player, game, phone, count( distinct phone) over ( partition by player ) as no_phones
3 FROM t2)
4 PIVOT (count(phone) AS cnt FOR (game) IN ('1a','1b','2a','2b','3a','3c'));
PLAYER NO_PHONES '1a'_CNT '1b'_CNT '2a'_CNT '2b'_CNT '3a'_CNT '3c'_CNT
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
ccc 1 0 0 0 1 0 0
ddd 2 1 0 0 0 0 0
aaa 3 2 1 0 0 0 0
bbb 2 0 0 1 1 0 0
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.