Access path between parent and child table in Oracle - sql

I have parent and child tables as shown below.
Child_table | Reference_colums | Parent_table | Referenced_columns
------------|------------------|--------------|-------------------
T1 | Clan_Id | T0 | Clan_Id
X1 | Clan_Id | T0 | Clan_Id
X2 | Clan_Id | T0 | Clan_Id
T2 | Clan_Id | T1 | Clan_Id
Y1 | Clan_Id | T1 | Clan_Id
Y2 | Clan_Id | X1 | Clan_Id
T3 | C31 | T2 | C2
T4 | C4 | T3 | C32
If I give input (Parent table = T0 an Child table as T4)
I should get output as
Child_table | Reference_colums | Parent_table | Referenced_columns
------------|------------------|--------------|-------------------
T1 | Clan_Id | T0 | Clan_Id
T2 | Clan_Id | T1 | Clan_Id
T3 | C31 | T2 | C2
T4 | C4 | T3 | C32
As access path between T4 and T0 is
T0 ->T1->T2->T3->T4
Can you please help me to write the SELECT query in Oracle

As mentioned by #DavidAldrige you need to use the Oracle's CONNECT BY that handles hierarchical queries.
SELECT *
FROM tab
START WITH child = 'T4'
CONNECT BY prior parent = child and child != 'T0';
sqlfiddle demo

There is absolutely nothing wrong with using Oracle's connect by clause. I have been trying to use Common Table Expressions (CTE) instead because they are not proprietary to Oracle. The skill translates to other databases, which in my case is SQL server. The following query will give you the results you desire, but using a CTE instead of connect by.
The DATASET clause is just setting up the sample data, FINDSET is where the work is performed:
WITH
dataset
AS
(SELECT 'T1' AS child_table, 'T0' AS parent_table
FROM DUAL
UNION ALL
SELECT 'X1' AS child_table, 'T0' AS parent_table
FROM DUAL
UNION ALL
SELECT 'X2' AS child_table, 'T0' AS parent_table
FROM DUAL
UNION ALL
SELECT 'T2' AS child_table, 'T1' AS parent_table
FROM DUAL
UNION ALL
SELECT 'Y1' AS child_table, 'T1' AS parent_table
FROM DUAL
UNION ALL
SELECT 'Y2' AS child_table, 'X1' AS parent_table
FROM DUAL
UNION ALL
SELECT 'T3' AS child_table, 'T2' AS parent_table
FROM DUAL
UNION ALL
SELECT 'T4' AS child_table, 'T3' AS parent_table
FROM DUAL),
findset (parent_table, child_table)
AS
(SELECT parent_table, child_table
FROM dataset
WHERE child_table = 'T4'
UNION ALL
SELECT dataset.parent_table, dataset.child_table
FROM findset INNER JOIN dataset ON dataset.child_table =
findset.parent_table)
SELECT *
FROM findset;
This gives the following result:
PARENT_TABLE CHILD_TABLE
T3 T4
T2 T3
T1 T2
T0 T1

Related

joining temporal tables in oracle

I am looking for better solutions to a fairly generic problem with temporal tables.
say we have
table_a (some_value int, date_from date, date_to date)
and a series of similar tables table_b, table_c, ...
the actual system is an HR system that tracks persons, contracts, assignments, salaries, ... all they are all dated in the way described above.
I need to join such tables (e.g. imagine getting all rows with the same some_value) and return the period for which such join is valid (that is all the rows overlap over such period).
with two tables it is easy (ignore NULL values for the time being)
select a.some_value, greatest(a.date_from, b.date_from) date_from, least(a.date_to, b.date_to) date_to
from table_a a join table_b b on a.some_value = b.some_value
where a.date_from < b.date_to and b.date_from < a.date_to
this become quadratically harder with more tables, because with three table (A, B, C) you need to check the overlap between A and B, B and C, C and A. with N tables this grows as N^2.
so I have written a pl/sql pipelined function (call it dated_join) that given two intervals it returns one row with the overlapping period or nothing if it doesn't overlap
so I can have for three tables
select a.some_value, period_b.date_from, period_b.date_to
from table_a a
join table_b b on a.some_value = b.some_value
join table(dated_join(a.date_from, a.date_to, b.date_from, b.date_to)) period_a
join table_c c on a.some_value = c.some_value
join table(dated_join(period_a.date_from, period_a.date_to, c.date_from, c.date_to)) period_b
this scales linearly to N values, because each period is joined only with the previous one and it carries forward the overlapping period.
question: is it possible to make this strategy work with OUTER JOINs? I cannot find any half-decent solution.
is there anything in the SQL:2011 temporal extensions that would help with this?
thanks for your help
If you want to join multiple table so that they all overlap the same period then you can use GREATEST and LEAST:
SELECT t1.date_from AS t1_from,
t2.date_from AS t2_from,
t3.date_from AS t3_from,
t4.date_from AS t4_from,
t1.date_to AS t1_to,
t2.date_to AS t2_to,
t3.date_to AS t3_to,
t4.date_to AS t4_to
FROM table1 t1
INNER JOIN table2 t2
ON ( t1.date_to > t2.date_from
AND t1.date_from < t2.date_to )
INNER JOIN table3 t3
ON ( LEAST( t1.date_to, t2.date_to ) > t3.date_from
AND GREATEST( t1.date_from, t2.date_from ) < t3.date_to )
INNER JOIN table4 t4
ON ( LEAST( t1.date_to, t2.date_to, t3.date_to ) > t4.date_from
AND GREATEST( t1.date_from, t2.date_from, t3.date_from ) < t4.date_to );
Which, for the sample data:
CREATE TABLE table1 ( date_from, date_to ) AS
SELECT DATE '2020-01-01', DATE '2020-01-10' FROM DUAL UNION ALL
SELECT DATE '2020-02-10', DATE '2020-02-15' FROM DUAL UNION ALL
SELECT DATE '2020-03-15', DATE '2020-03-18' FROM DUAL;
CREATE TABLE table2 ( date_from, date_to ) AS
SELECT DATE '2020-01-05', DATE '2020-01-15' FROM DUAL UNION ALL
SELECT DATE '2020-02-09', DATE '2020-02-16' FROM DUAL UNION ALL
SELECT DATE '2020-03-16', DATE '2020-03-18' FROM DUAL;
CREATE TABLE table3 ( date_from, date_to ) AS
SELECT DATE '2020-01-01', DATE '2020-01-02' FROM DUAL UNION ALL
SELECT DATE '2020-01-09', DATE '2020-01-16' FROM DUAL UNION ALL
SELECT DATE '2020-02-08', DATE '2020-02-17' FROM DUAL UNION ALL
SELECT DATE '2020-03-15', DATE '2020-03-17' FROM DUAL;
CREATE TABLE table4 ( date_from, date_to ) AS
SELECT DATE '2020-01-02', DATE '2020-01-12' FROM DUAL UNION ALL
SELECT DATE '2020-02-08', DATE '2020-02-17' FROM DUAL UNION ALL
SELECT DATE '2020-03-16', DATE '2020-03-19' FROM DUAL;
Outputs:
T1_FROM | T2_FROM | T3_FROM | T4_FROM | T1_TO | T2_TO | T3_TO | T4_TO
:-------- | :-------- | :-------- | :-------- | :-------- | :-------- | :-------- | :--------
15-MAR-20 | 16-MAR-20 | 15-MAR-20 | 16-MAR-20 | 18-MAR-20 | 18-MAR-20 | 17-MAR-20 | 19-MAR-20
10-FEB-20 | 09-FEB-20 | 08-FEB-20 | 08-FEB-20 | 15-FEB-20 | 16-FEB-20 | 17-FEB-20 | 17-FEB-20
01-JAN-20 | 05-JAN-20 | 09-JAN-20 | 02-JAN-20 | 10-JAN-20 | 15-JAN-20 | 16-JAN-20 | 12-JAN-20
and, if you want it so that there is an overlap with any part of the ranges, then swap the GREATEST and LEAST:
SELECT t1.date_from AS t1_from,
t2.date_from AS t2_from,
t3.date_from AS t3_from,
t4.date_from AS t4_from,
t1.date_to AS t1_to,
t2.date_to AS t2_to,
t3.date_to AS t3_to,
t4.date_to AS t4_to
FROM table1 t1
INNER JOIN table2 t2
ON ( t1.date_to > t2.date_from
AND t1.date_from < t2.date_to )
INNER JOIN table3 t3
ON ( GREATEST( t1.date_to, t2.date_to ) > t3.date_from
AND LEAST( t1.date_from, t2.date_from ) < t3.date_to )
INNER JOIN table4 t4
ON ( GREATEST( t1.date_to, t2.date_to, t3.date_to ) > t4.date_from
AND LEAST( t1.date_from, t2.date_from, t3.date_from ) < t4.date_to );
Which outputs:
T1_FROM | T2_FROM | T3_FROM | T4_FROM | T1_TO | T2_TO | T3_TO | T4_TO
:-------- | :-------- | :-------- | :-------- | :-------- | :-------- | :-------- | :--------
15-MAR-20 | 16-MAR-20 | 15-MAR-20 | 16-MAR-20 | 18-MAR-20 | 18-MAR-20 | 17-MAR-20 | 19-MAR-20
10-FEB-20 | 09-FEB-20 | 08-FEB-20 | 08-FEB-20 | 15-FEB-20 | 16-FEB-20 | 17-FEB-20 | 17-FEB-20
01-JAN-20 | 05-JAN-20 | 01-JAN-20 | 02-JAN-20 | 10-JAN-20 | 15-JAN-20 | 02-JAN-20 | 12-JAN-20
01-JAN-20 | 05-JAN-20 | 09-JAN-20 | 02-JAN-20 | 10-JAN-20 | 15-JAN-20 | 16-JAN-20 | 12-JAN-20
db<>fiddle here
If you want all values from tables that overlap with the timeperiod, then you seem to want:
select *
from table_a a left join
table_b b
on b.date_from < a.date_to and
b.date_to > a.date_from left join
table_c c
on c.date_from < a.date_to and
c.date_to > a.date_from
where a.value = ?
I don't see any "quadratic" complexity. Exactly the same join conditions are used for each new table.
improving on the solution given above by #MT0 we can use a different overlap test
GREATEST(t1.date_from, t2.date_from) < LEAST(t1.date_to, t2.date_to)
and extend it to any number of tables
GREATEST(t1.date_from, t2.date_from, t3.date_from, t4.date_from, ...)
< LEAST(t1.date_to, t2.date_to, t3.date_to, t4.date_to, ...)
the inner join can then be replaced by one single test
SELECT t1.date_from AS t1_from,
t2.date_from AS t2_from,
t3.date_from AS t3_from,
t4.date_from AS t4_from,
t1.date_to AS t1_to,
t2.date_to AS t2_to,
t3.date_to AS t3_to,
t4.date_to AS t4_to
FROM table1 t1
CROSS JOIN table2 t2
CROSS JOIN table3 t3
CROSS JOIN table4 t4
WHERE
GREATEST(t1.date_from, t2.date_from, t3.date_from, t4.date_from)
< LEAST(t1.date_to, t2.date_to, t3.date_to, t4.date_to);
while the outer join becomes
SELECT t1.date_from AS t1_from,
t2.date_from AS t2_from,
t3.date_from AS t3_from,
t4.date_from AS t4_from,
t1.date_to AS t1_to,
t2.date_to AS t2_to,
t3.date_to AS t3_to,
t4.date_to AS t4_to
FROM table1 t1
left outer join table2 t2 on
greatest(t1.date_from, t2.date_from) < least(t1.date_to, t2.date_to)
left outer join table3 t3 on
greatest(t1.date_from, t2.date_from, t3.date_from) < least(t1.date_to, t2.date_to, t3.date_to)
left outer join table4 t4 on
greatest(t1.date_from, t2.date_from, t3.date_from, t4.date_from) < least(t1.date_to, t2.date_to, t3.date_to, t4.date_to);
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=e43b7c58208b7ea85af24ec0ec0ca4a7

How to select first x records from second table

I would like to get all records from first table and only x records from second table.
How many records from second table I have info in first table :
My tables are
table1 :
WITH table1(a,b) AS
(
SELECT 'aa',3 FROM dual UNION ALL
SELECT 'bb',2 FROM dual UNION ALL
SELECT 'cc',4 FROM dual
)
SELECT *
FROM table1;
a | b (number of records from table2 (x))
------
aa | 3
bb | 2
cc | 4
table2 :
WITH table2(a,b) AS
(
SELECT 'aa','1xx' FROM dual UNION ALL
SELECT 'aa','2yy' FROM dual UNION ALL
SELECT 'aa','3ww' FROM dual UNION ALL
SELECT 'aa','4zz' FROM dual UNION ALL
SELECT 'aa','5qq' FROM dual UNION ALL
SELECT 'bb','1aa' FROM dual UNION ALL
SELECT 'bb','2bb' FROM dual UNION ALL
SELECT 'bb','3cc' FROM dual UNION ALL
SELECT 'cc','1oo' FROM dual UNION ALL
SELECT 'cc','2uu' FROM dual UNION ALL
SELECT 'cc','3tt' FROM dual UNION ALL
SELECT 'cc','4zz' FROM dual UNION ALL
SELECT 'cc','5rr' FROM dual
)
SELECT *
FROM table2;
a | b
--------
aa | 1xx
aa | 2yy
aa | 3ww
aa | 4zz
aa | 5qq
bb | 1aa
bb | 2bb
bb | 3cc
bb | 4dd
bb | 5ee
cc | 1oo
cc | 2uu
cc | 3tt
cc | 4zz
cc | 5rr
Expected Result:
a | b
--------
aa | 1xx
aa | 2yy
aa | 3ww
bb | 1aa
bb | 2bb
cc | 1oo
cc | 2uu
cc | 3tt
cc | 4zz
You can use ROW_NUMBER() analytic function with LEFT/RIGHT OUTER JOIN among the tables :
WITH t2 AS
(
SELECT t2.a,t2.b, ROW_NUMBER() OVER (PARTITION BY t2.a ORDER BY t2.b) AS rn
FROM table2 t2
)
SELECT t2.a, t2.b
FROM t2
LEFT JOIN table1 t1
ON t1.a = t2.a
WHERE rn <= t1.b
Demo
You need to write something like:
SELECT a,
b
FROM Table2 T,
( SELECT LEVEL L FROM DUAL
CONNECT BY LEVEL <= (SELECT MAX(b) FROM Table1)
) A
WHERE T.b>= A.L
ORDER BY T.a;
Ideally you should have a ordering column in table2. When you say first X rows it does not make any sense unless you have something like an id or date field to order the records.
Anyway, assuming the number part of the column b in table 2 for ordering and assuming the number would be followed by 2 characters only such as xx,yy etc you can use the logic below
Select Tb1.a, Tb1.b
from
(Select t.*, row_number() over (partition by a order by substr(b,1,length(b)-2)) as seq
from Table2 t) Tb1
join Table1 Tb2
on Tb1.a = Tb2.a
Where Tb1.seq <= Tb2.b;
Demo - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=3030b2372bcbb007606bbb6481af9884
Again it's just a job for lateral:
WITH prep AS
(
SELECT *
FROM tab1,
LATERAL
(
SELECT LEVEL AS lvl
FROM dual
CONNECT BY LEVEL <= b
)
)
SELECT p.a, t2.b
FROM prep p
JOIN tab2 t2
ON p.lvl = regexp_substr(t2.b,'^\d+')
AND p.a = t2.a
ORDER BY p.a, p.lvl

Recursive sql query in oracle

Table: ID1 and ID2 are name of the column
| ID1 | ID2 |
| 4 | 3 |
| 3 | 2 |
| 2 | 1 |
| 7 | 6 |
| 6 | 5 |
| 9 | 8 |
Desired Result
| ID1 | ID2 |
| 4 | 1 |
| 7 | 5 |
| 9 | 8 |
I need to build a recursive sql query for oracle using connect by or recursive cte. Unable to figure out solution.
No need to use CTE in this case since you do not do any cumulative calculations while traversing the tree.
SQL> with t(id1, id2) as
2 (select 4,3 from dual
3 union all select 3,2 from dual
4 union all select 2,1 from dual
5 union all select 7,6 from dual
6 union all select 6,5 from dual
7 union all select 9,8 from dual)
8 select connect_by_root id1 id1, id2
9 from t
10 where connect_by_isleaf = 1
11 start with not exists (select null from t t0 where t0.id2 = t.id1)
12 connect by prior id2 = id1;
ID1 ID2
---------- ----------
4 1
7 5
9 8
This is just a supplement answer without using Hierarchical queries which removes common elements from id1 and id2.
WITH t(id1, id2)
AS (SELECT 4,
3
FROM dual
UNION ALL
SELECT 3,
2
FROM dual
UNION ALL
SELECT 2,
1
FROM dual
UNION ALL
SELECT 7,
6
FROM dual
UNION ALL
SELECT 6,
5
FROM dual
UNION ALL
SELECT 9,
8
FROM dual),
t1
AS (SELECT id1 id1,
id1 id2
FROM t),
t2
AS (SELECT id2 id1,
id2 id2
FROM t),
t3
AS (SELECT ROWNUM row_num1,
id1
FROM (SELECT ( t.id1 ) id1
FROM t
WHERE NOT EXISTS (SELECT NULL
FROM t1,
t2
WHERE ( t1.id1 = t2.id1
AND t1.id2 = t2.id2 )
AND ( t.id1 = t1.id1 ))
ORDER BY t.id1 ASC)),
t4
AS (SELECT ROWNUM row_num1,
id2
FROM (SELECT ( t.id2 ) id2
FROM t
WHERE NOT EXISTS (SELECT NULL
FROM t1,
t2
WHERE ( t1.id1 = t2.id1
AND t1.id2 = t2.id2 )
AND ( t.id2 = t2.id2 ))
ORDER BY t.id2 ASC))
SELECT a.id1,
b.id2
FROM t3 a,
t4 b
WHERE a.row_num1 = b.row_num1
ORDER BY id1;

Eliminating NOT IN from query

I'm trying to eliminate the need to use NOT IN in my query:
select count(*)
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.DATAID = T2.EXISTING_DOCUMENT
AND T1.ownerid = -2000
AND T1.SUBTYPE = 144
AND T1.dataid NOT IN (SELECT T3.dataid
FROM TABLE3 T3
WHERE T3.ID = 123)
Reason: I read that NOT IN is slow (+500k rows) and doesn't use indices
I tried:
select count(*)
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.DATAID = T2.EXISTING_DOCUMENT
AND T1.ownerid = -2000
AND T1.SUBTYPE = 144
left outer join TABLE3 T3
on T3.ancestorid = T1.dataid
where T3.ID = 123
NOT IN does use indices, at least in a competent database such as Oracle. However, you can write this using joins if you prefer.
But, why doesn't this do what you want?
select count(*)
FROM TABLE1 T1
WHERE T1.ownerid = -2000 AND T1.SUBTYPE = 144;
You are using a LEFT JOIN, so the only difference is that your version counts duplicates in TABLE2. But that may not really apply.
Your query doesn't really make sense, because you are comparing T1.dataid to T1.dataid. But, further, the comparison to Table3 has no impact on the result. So, you can just remove it:
select count(*)
FROM TABLE1 T1 LEFT OUTER JOIN
TABLE2 T2
ON T1.DATAID = T2.EXISTING_DOCUMENT AND
T1.ownerid = -2000 AND
T1.SUBTYPE = 144 ;
Because of the LEFT JOIN, filtering in the ON clause will not remove any rows. And because it is NOT IN, there is no possibility of duplication.
Use a WHERE x IS NULL filter to emulate a NOT IN.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE t1 ( ownerid int, subtype int, dataid int, note varchar(100) ) ;
INSERT INTO t1 ( ownerid, subtype, dataid, note )
SELECT 1 as ownerid, 1 as subtype, 1 as dataid, 'WHERE Filter' as note FROM DUAL UNION ALL
SELECT -2000, 1,1, 'IN WHERE Filter' FROM DUAL UNION ALL
SELECT -2000,144,1, 'IN WHERE, NOT IN t3' FROM DUAL UNION ALL
SELECT -2000,144,2, 'IN WHERE, IN t3' FROM DUAL UNION ALL
SELECT -2000,144,3, 'IN WHERE, NOT IN t3' FROM DUAL
;
CREATE TABLE t2 ( existing_document int, note varchar(100) ) ;
INSERT INTO t2 (existing_document, note)
SELECT 1 as existing_document, 'JOIN t1' as note FROM DUAL UNION ALL
SELECT 2, 'JOIN t1' FROM DUAL UNION ALL
SELECT 2, 'JOIN t1, DUPE' FROM DUAL UNION ALL
SELECT 3, 'JOIN t1' FROM DUAL UNION ALL
SELECT 3, 'JOIN t1, DUPE' FROM DUAL UNION ALL
SELECT 4, 'NOT JOIN t1' FROM DUAL
;
CREATE TABLE t3 ( id int, dataid int, note varchar(100) ) ;
INSERT INTO t3 (id, dataid, note)
SELECT 1 as id, 1 as dataid, 'No filter. No match.' as note FROM DUAL UNION ALL
SELECT 1, 4, 'No filter. No match t1.' FROM DUAL UNION ALL
SELECT 123,2,'Match JOIN filter. Match t1' FROM DUAL
;
Read the notes in the setup to view how I'm building up the data. It's very simple and not a lot to count, but it should give you an idea on how this data works together.
Query:
SELECT * /* Not counting here so you can see what's supposed to be counted. */
FROM t1
INNER JOIN t2 ON t1.dataid = t2.EXISTING_DOCUMENT
LEFT OUTER JOIN t3 ON t1.dataid = t3.dataid
AND t3.ID = 123
WHERE t1.ownerid = -2000
AND t1.subtype = 144
AND t3.dataid IS NULL /* This is the NOT IN */
Results:
| OWNERID | SUBTYPE | DATAID | NOTE | EXISTING_DOCUMENT | NOTE | ID | DATAID | NOTE |
|---------|---------|--------|---------------------|-------------------|---------------|--------|--------|--------|
| -2000 | 144 | 1 | IN WHERE, NOT IN t3 | 1 | JOIN t1 | (null) | (null) | (null) |
| -2000 | 144 | 3 | IN WHERE, NOT IN t3 | 3 | JOIN t1 | (null) | (null) | (null) |
| -2000 | 144 | 3 | IN WHERE, NOT IN t3 | 3 | JOIN t1, DUPE | (null) | (null) | (null) |
The optimizer usually runs very well with the WHERE x IS NULL syntax and indexes should still apply, but if Oracle is able to make use of the indexes in the NOT IN, that is a big plus. If you're dealing with a lot of data, the IS NULL method can be a lot faster. The best check is to just test it with your actual data.
how about NOT EXISTS?
select count(*)
FROM TABLE1 T1
LEFT OUTER JOIN TABLE2 T2
ON T1.DATAID = T2.EXISTING_DOCUMENT
AND T1.ownerid = -2000
AND T1.SUBTYPE = 144
AND NOT EXISTS (SELECT 1
FROM TABLE3 T3
WHERE T3.ID = 123
AND T3.dataid = T1.dataid)

Oracle SELECT query to find the smallest distinct delimited string

I have below table
C1 | Path
--------|-----------
T1 | T0/T1
T2 | T0/T1/T2
T3 | T0/T1/T2/T3
X1 | T0/X0/X1
T2 | T0/X0/X1/T2
T3 | T0/X0/X1/T2/T3
T3 | T0/Y0/Y1/Y2/T3
Y3 | T0/X0/X2/Y3
Y4 | T0/X0/X2/Y3/Y4
I need to write q query or code which should return smallest distinct delimited string as shown below
C1 | Path
--------|-----------
T1 | T0/T1
X1 | T0/X0/X1
T3 | T0/Y0/Y1/Y2/T3
Y3 | T0/X0/X2/Y3
Ex: T0/T1 is a substring of T0/T1/T2 and T0/T1/T2/T3.
T0/Y0/Y1/Y2/T3 is not substring of any other string. It is distinct.
Can you please help me to write this query in oracle
Try:
SELECT t1.*
FROM Table1 t1
LEFT JOIN Table1 t2
ON t1.Path <> t2.Path
AND t1.path LIKE '%'||t2.Path||'%'
WHERE t2.c1 IS NULL
Demo: http://sqlfiddle.com/#!4/a3f87/3
| C1 | PATH |
|----|----------------|
| T1 | T0/T1 |
| X1 | T0/X0/X1 |
| T3 | T0/Y0/Y1/Y2/T3 |
| Y3 | T0/X0/X2/Y3 |
This query works http://sqlfiddle.com/#!4/bf32e/19
With X as
(select 'T0/T1' c1 from dual
union
select 'T0/T1/T2' c1 from dual
union
select 'T0/T1/T2/T3' c1 from dual
union
select 'T0/X0/X1' c1 from dual
union
select 'T0/X0/X1/T2' c1 from dual
union
select 'T0/X0/X1/T2/T3' c1 from dual
union
select 'T0/Y0/Y1/Y2/T3' c1 from dual
union
select 'T0/X0/X2/Y3' c1 from dual
union
select 'T0/X0/X2/Y3/Y4' c1 from dual),
Y as (
select distinct X1.C1 YC1 from X X1, X X2
where X1.C1 like '%'||X2.C1||'%' and X1.C1<>X2.C1)
select * from X where not exists
(select 1 from Y where YC1=X.C1)