Oracle SQL nested relationship into on level - sql

If I have the following table:
ColumnA ColumnB
A B
C D
D E
B C
How can I write a sql to get the following:
ColumnC ColumnD
A B
A C
A D
A E
B C
B D
B E
C D
C E
D E
The level of nested relationship many be multiple.
Can sql handle this? Or need PLSQL?

Use a hierarchical query with CONNECT_BY_ROOT:
SELECT CONNECT_BY_ROOT( ColumnA ) AS ColumnA,
ColumnB
FROM table_name
CONNECT BY PRIOR ColumnB = ColumnA
For your data:
CREATE TABLE table_name ( ColumnA, ColumnB ) AS
SELECT 'A', 'B' FROM DUAL UNION ALL
SELECT 'C', 'D' FROM DUAL UNION ALL
SELECT 'D', 'E' FROM DUAL UNION ALL
SELECT 'B', 'C' FROM DUAL;
This outputs:
COLUMNA | COLUMNB
:------ | :------
A | B
A | C
A | D
A | E
B | C
B | D
B | E
C | D
C | E
D | E
db<>fiddle here

A recursive CTE might help.
SQL> with
2 test (cola, colb) as
3 -- sample data; you already have that
4 (select 'a', 'b' from dual union all
5 select 'c', 'd' from dual union all
6 select 'd', 'e' from dual union all
7 select 'b', 'c' from dual
8 ),
9 -- recursive CTE
10 temp (parent, child) as
11 (select t.cola parent, t.colb child
12 from test t
13 union all
14 select a.parent, b.colb child
15 from temp a join test b on a.child = b.cola
16 )
17 select *
18 from temp
19 order by parent, child;
PARENT CHILD
---------- ----------
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
10 rows selected.
SQL>

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>

How to write SQL join to find description of id using Oracle?

I have 2 input tables, and I need output in string format.
I tried following query, but it does not work. How can I get the above output?
with
cte1 as --table 1
(select 1 as id , 'A' as abc from dual
union
select 2 as id , 'B' as abc from dual
union
select 3 as id , 'C' as abc from dual
union
select 4 as id , 'D' as abc from dual
union
select 5 as id , 'E' as abc from dual
union
select 6 as id , 'F' as abc from dual
),
cte2 as --table2
(select 1 as id, 3 as name from dual
union
select 1 as id, 5 as name from dual
union
select 1 as id, 4 as name from dual
union
select 2 as id, 3 as name from dual
union
select 2 as id, 6 as name from dual
)
SELECT e.id, e.abc, m.id as mgr, e.abc, c.*
FROM
cte1 e, cte2 m, cte2 c
WHERE e.id = m.id
and
e.id=c.name;
You are trying to join each row in table 1 to two rows in table 2, and the conditions can never both be true.
You want to join each row in table 2 to two rows in table 1:
SELECT e.abc, m.abc
FROM cte2 c, cte1 e, cte1 m
WHERE e.id = c.id
AND m.id = c.name
ORDER BY c.id, c.name;
A A
- -
A C
A D
A E
B C
B F
or with 'modern' join syntax, which you should really be using:
SELECT e.abc, m.abc
FROM cte2 c
JOIN cte1 e ON e.id = c.id
JOIN cte1 m ON m.id = c.name
ORDER BY c.id, c.name;
A A
- -
A C
A D
A E
B C
B F

Extracting indrect relation in SQL

I have this table
ID S E
1 a b
1 b c
2 a b
2 b d
2 b e
2 e d
3 a c
3 c f
I want to extract indirect relations from this table for example:
ID S E
1 a c
2 a d
2 a e
2 b d
3 a f
I could do it in C# or Java but I am wondering if there is a solution for doing it in SQL or not.
You can try this...
Create table #a (ID int, S varchar(1), E varchar(1))
Insert into #a
select 1, 'a', 'b' Union all
select 1, 'b', 'c' Union all
select 2, 'a', 'b' Union all
select 2, 'b', 'd'Union all
select 2, 'b', 'e'Union all
select 2, 'e', 'd'Union all
select 3, 'a', 'c'Union all
select 3, 'c', 'f'
select * from #a
select a.S, b.E
from #a a
Inner join #a b on a.E = b.S and a.ID = b.Id
drop table #a

Oracle sql query

I am dealing with tables which (for the purposes of displaying here) look like the following:
A
A_ID | Clob_Col
1 | value
2 | value
3 | null
4 | value
5 | null
6 | value
7 | value
8 | null
9 | value
10 | value
B
B_ID |A_ID | C_ID
10 | 1 | 20
11 | 2 | 20
12 | 6 | 21
13 | 7 | 22
14 | 8 | 22
15 | 9 | 23
C
C_ID
20
21
22
23
24
25
(All tables have more columns)
I wish to write a query which will return values from all three tables but exclude records where certain values match those in Table C (NOT IN).
My problem is to return the values from Table A which do not link to Table B when using a NOT IN clause on Table C.
Example:
SELECT a.A_ID, a.Clob_Col, b.B_ID, c.C_ID
from A a
LEFT JOIN B b on a.A_ID=b.A_ID
LEFT JOIN C c on b.C_ID=c.C_ID
WHERE a.AID >= 2
AND a.AID <= 7
AND c.C_ID NOT IN (22, 23, 24)
The last line - c.C_ID NOT IN (22, 23, 24) - will leave us with the following records in the B table: b_BID = 10,11 or 12
In turn, these link to the following records in Table A: a.ID = 1,2 and 6.
The first clause - a.AID >= 2 AND a.AID <= 7 - further restricts our final result to a.ID = 2 and 6.
The final result will look like:
A_ID |Clob_Col |B_ID |C_ID
2 |value |11 |20
6 |value |12 |21
But I also want to return the A table records which don't link to Table B - a.ID 3, 4 and 5
So I expect my results to be
A_ID |Clob_Col |B_ID |C_ID
2 |value |11 |20
3 |null |null |null
4 |value |null |null
5 |null |null |null
6 |value |12 |21
Note: The reason I included the Clob value is that someone suggested selecting all records and then performing a MINUS operation on the records where c.C_ID IN (22, 23, 24).
This seemed reasonable however Oracle does not allow MINUS operation where Clob columns are involved.
I think you forgot to use "on" clause for join. You can try this :
SELECT a.A_ID, a.Clob_Col, b.B_ID, c.C_ID
from A a
LEFT JOIN B b on a.A_ID=b.A_ID
LEFT JOIN C c on b.C_ID=c.C_ID
WHERE a.A_ID between 2 and 7
AND c.C_ID NOT IN (22, 23, 24)
Hope it will work.
Include what your joining on and also you can use BETWEEN for the first WHERE clause.
Also i would use INNER JOINS rather then LEFT as per your data.
SELECT a.A_ID, a.Clob_Col, b.B_ID, c.C_ID
FROM A a
INNER JOIN B b ON a.A_ID = b.B_ID
INNER JOIN C c ON b.C_ID = c.C_ID
WHERE a.AID BETWEEN 2 AND 7
AND c.C_ID NOT IN (22, 23, 24)
I think this does what you're after:
with a as (select 1 a_id, 'val1' clob_col from dual union all
select 2 a_id, 'val2' clob_col from dual union all
select 3 a_id, null clob_col from dual union all
select 4 a_id, 'val4' clob_col from dual union all
select 5 a_id, null clob_col from dual union all
select 6 a_id, 'val6' clob_col from dual union all
select 7 a_id, 'val7' clob_col from dual union all
select 8 a_id, null clob_col from dual union all
select 9 a_id, 'val9' clob_col from dual union all
select 10 a_id, 'val10' clob_col from dual),
b as (select 10 b_id, 1 a_id, 20 c_id from dual union all
select 11 b_id, 2 a_id, 20 c_id from dual union all
select 12 b_id, 6 a_id, 21 c_id from dual union all
select 13 b_id, 7 a_id, 22 c_id from dual union all
select 14 b_id, 8 a_id, 22 c_id from dual union all
select 15 b_id, 9 a_id, 23 c_id from dual),
c as (select 20 c_id from dual union all
select 21 c_id from dual union all
select 22 c_id from dual union all
select 23 c_id from dual union all
select 24 c_id from dual union all
select 25 c_id from dual)
select a.a_id, a.clob_col, b.b_id, c.c_id
from a
left outer join b on (a.a_id = b.a_id)
left outer join c on (b.c_id = c.c_id)
where a.a_id between 2 and 7
and (c.c_id not in (22, 23, 24) or c.c_id is null)
order by a.a_id;
A_ID CLOB_COL B_ID C_ID
---------- -------- ---------- ----------
2 val2 11 20
3
4 val4
5
6 val6 12 21
and if c_id is 27 for a_id = 6 in the b table:
A_ID CLOB_COL B_ID C_ID
---------- -------- ---------- ----------
2 val2 11 20
3
4 val4
5
6 val6 12
You have to take account of the fact that c_id could be null, as well as not being in the set of values being excluded.
ETA: Thanks to Ponder Stibbons' suggestion in the comments, if you didn't want the row to be displayed where a.a_id = b.a_id matches but there isn't a match on b.c_id = c.c_id, then changing the or c.c_id is null to or b.c_id is null removes that row:
with a as (select 1 a_id, 'val1' clob_col from dual union all
select 2 a_id, 'val2' clob_col from dual union all
select 3 a_id, null clob_col from dual union all
select 4 a_id, 'val4' clob_col from dual union all
select 5 a_id, null clob_col from dual union all
select 6 a_id, 'val6' clob_col from dual union all
select 7 a_id, 'val7' clob_col from dual union all
select 8 a_id, null clob_col from dual union all
select 9 a_id, 'val9' clob_col from dual union all
select 10 a_id, 'val10' clob_col from dual),
b as (select 10 b_id, 1 a_id, 20 c_id from dual union all
select 11 b_id, 2 a_id, 20 c_id from dual union all
select 12 b_id, 6 a_id, 27 c_id from dual union all
select 13 b_id, 7 a_id, 22 c_id from dual union all
select 14 b_id, 8 a_id, 22 c_id from dual union all
select 15 b_id, 9 a_id, 23 c_id from dual),
c as (select 20 c_id from dual union all
select 21 c_id from dual union all
select 22 c_id from dual union all
select 23 c_id from dual union all
select 24 c_id from dual union all
select 25 c_id from dual)
select a.a_id, a.clob_col, b.b_id, c.c_id
from a
left outer join b on (a.a_id = b.a_id)
left outer join c on (b.c_id = c.c_id)
where a.a_id between 2 and 7
and (c.c_id not in (22, 23, 24) or b.c_id is null)
order by a.a_id;

Oracle advanced union

Is there any advanced Oracle SQL methods to solve this kind of situation?
Simplified:
Two queries returns primary_key_value and other_value.
Both queries always return primary_key_value but other_value might be null.
So how I can union those two queries so that it returns always those rows which has other_value, but if both queries are having other_value = null with same primary key, then only one row should be returned.
I know this is so stupid case. But specifications were like this :)
Example:
First query:
A | B
=======
1 | X
2 |
3 |
4 | Z
Second query:
A | B
=======
1 | Y
2 |
3 | Z
4 |
So result need to be like this:
A | B
=======
1 | X
1 | Y
2 |
3 | Z
4 | Z
You could use analytics:
SQL> WITH q1 AS (
2 SELECT 1 a, 'X' b FROM DUAL UNION ALL
3 SELECT 2 a, '' b FROM DUAL UNION ALL
4 SELECT 3 a, '' b FROM DUAL UNION ALL
5 SELECT 4 a, 'Z' b FROM DUAL
6 ), q2 AS (
7 SELECT 1 a, 'Y' b FROM DUAL UNION ALL
8 SELECT 2 a, '' b FROM DUAL UNION ALL
9 SELECT 3 a, 'Z' b FROM DUAL UNION ALL
10 SELECT 4 a, '' b FROM DUAL
11 )
12 SELECT a, b
13 FROM (SELECT a, b,
14 rank() over(PARTITION BY a
15 ORDER BY decode(b, NULL, 2, 1)) rnk
16 FROM (SELECT * FROM q1
17 UNION
18 SELECT * FROM q2))
19 WHERE rnk = 1;
A B
---------- -
1 X
1 Y
2
3 Z
4 Z
If you want use something really advanced, use model clause http://rwijk.blogspot.com/2007/10/sql-model-clause-tutorial-part-one.html
But, in real life, using such things usually means bad-designed data model
Another way to look at is that you want all possible values from the union of column A then left outer outer join these with the non-null values from column B, thus only showing null in B when there is no non-null value to display.
roughly:
WITH q1 as (whatever),
q2 as (whatever)
SELECT All_A.A, BVals.B
FROM (SELECT DISTINCT A FROM (SELECT A FROM q1 UNION SELECT A FROM q2)) All_A,
(SELECT A,B FROM q1 WHERE B IS NOT NULL
UNION
SELECT A,B FROM q2 WHERE B IS NOT NULL) BVals
WHERE All_A.A = BVals.A (+)
Also pruning the unwanted nulls explicitly could do the same job:
WITH q3 AS (q1_SELECT UNION q2_SELECT)
SELECT A,B
FROM q3 main
WHERE NOT ( B IS NULL AND
EXISTS (SELECT 1 FROM q3 x WHERE main.A = x.A and x.B IS NOT NULL) )