Selecting rows with exist operator using OR conditions - sql

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

Related

SQL logic to fail a check if any of the related customers has failed

I have the requirement to flag the customers Y only when all the related customers have also passed the check.
below are the two tables:
relationship table :
customer_id related_customer
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
11 11
11 22
22 11
22 22
Check table
customer_id check_flag
1 y
2 y
3 n
11 y
22 y
I want output like below:
customer_id paas_fail_flag
1 n
2 n
3 n
11 y
22 y
output justification: since 1,2,3 are related customers and since one of them (3) has n in table 2 , so all the related customers should also have n.
11,22 are related customers and both have y in table 2.so in output both should have y.
You need to join relationship to check and use conditional aggregation:
SELECT r.customer_id,
COALESCE(MAX(CASE WHEN c.check_flag = 'n' THEN c.check_flag END), 'y') paas_fail_flag
FROM relationship r INNER JOIN "check" c
ON c.customer_id = r.related_customer
GROUP BY r.customer_id
ORDER BY r.customer_id
See the demo.
Something like this? Sample data in lines #1 - 40; query begins at line #41:
SQL> WITH
2 -- sample data
3 rel (customer_id, related_customer)
4 AS
5 (SELECT 1, 1 FROM DUAL
6 UNION ALL
7 SELECT 1, 2 FROM DUAL
8 UNION ALL
9 SELECT 1, 3 FROM DUAL
10 UNION ALL
11 SELECT 2, 1 FROM DUAL
12 UNION ALL
13 SELECT 2, 2 FROM DUAL
14 UNION ALL
15 SELECT 2, 3 FROM DUAL
16 UNION ALL
17 SELECT 3, 1 FROM DUAL
18 UNION ALL
19 SELECT 3, 2 FROM DUAL
20 UNION ALL
21 SELECT 3, 3 FROM DUAL
22 UNION ALL
23 SELECT 11, 11 FROM DUAL
24 UNION ALL
25 SELECT 11, 22 FROM DUAL
26 UNION ALL
27 SELECT 22, 11 FROM DUAL
28 UNION ALL
29 SELECT 22, 22 FROM DUAL),
30 chk (customer_id, check_flag)
31 AS
32 (SELECT 1, 'y' FROM DUAL
33 UNION ALL
34 SELECT 2, 'y' FROM DUAL
35 UNION ALL
36 SELECT 3, 'n' FROM DUAL
37 UNION ALL
38 SELECT 11, 'y' FROM DUAL
39 UNION ALL
40 SELECT 22, 'y' FROM DUAL),
41 temp
42 AS
43 -- minimum CHECK_FLAG per customer and related customer
44 ( SELECT r.customer_id, r.related_customer, MIN (c.check_flag) mcf
45 FROM rel r JOIN chk c ON c.customer_id = r.related_customer
46 GROUP BY r.customer_id, r.related_customer)
47 SELECT customer_id, MIN (mcf) flag
48 FROM temp
49 GROUP BY customer_id
50 ORDER BY customer_id;
CUSTOMER_ID FLAG
----------- ----
1 n
2 n
3 n
11 y
22 y
SQL>
Assuming that your relationship data could be sparse, for example:
CREATE TABLE relationship ( customer_id, related_customer ) AS
SELECT 2, 3 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 3, 2 FROM DUAL UNION ALL
SELECT 11, 22 FROM DUAL;
CREATE TABLE "CHECK" ( customer_id, check_flag ) AS
SELECT 1, 'y' FROM DUAL UNION ALL
SELECT 2, 'y' FROM DUAL UNION ALL
SELECT 3, 'n' FROM DUAL UNION ALL
SELECT 11, 'y' FROM DUAL UNION ALL
SELECT 22, 'y' FROM DUAL;
(Note: The below query will also work on your dense data, where every relationship combination is enumerated.)
Then you can use a hierarchical query:
SELECT customer_id,
MIN(check_flag) AS check_flag
FROM (
SELECT CONNECT_BY_ROOT(c.customer_id) AS customer_id,
c.check_flag AS check_flag
FROM "CHECK" c
LEFT OUTER JOIN relationship r
ON (r.customer_id = c.customer_id)
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY NOCYCLE
( PRIOR r.related_customer = c.customer_id
OR PRIOR c.customer_id = r.related_customer )
AND PRIOR c.check_flag = 'y'
)
GROUP BY
customer_id
ORDER BY
customer_id
Which outputs:
CUSTOMER_ID
CHECK_FLAG
1
n
2
n
3
n
11
y
22
y
db<>fiddle here

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

How to get previous N rows for multiple joining rows

I am writing a SQL with Oracle Client 12 driver. I have two tables simplified as appended, and I want to get a table with following logic. The "B.TIME_B <= A0.TIME_A" seems created massive joining and made the query very slow. Please help to find best solution.
WITH A0 AS (
SELECT *
FROM A
WHERE A.EVENT = 'a0'
)
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY A0.TIME_A0 ORDER BY B.TIME_B DESC) RN,
A0.*,
B.*
FROM
A0,B
WHERE
B.TIME_B <= A0.TIME_A) B0
WHERE B0.RN <= 3
Find TIME_A, where EVENT_A = 'a0', as TIME_A0,
Find TIME_B = TIME_A0, as EVENT_B0,
And then get the row and previous 2 rows of table B, where EVENT_B0 found. N in this example is 3, and M is 2, but in real case both number are over 3000, so efficiency will be appreciated.
TableA
TIME_A EVENT_A
1 a1
2 a1
3 a1
4 a0
5 a2
6 a2
7 a3
8 a0
Table B
TIME_B EVENT_B
1 b1
2 b2
3 b3
4 b4
5 b5
6 b5
7 b6
8 b7
JOIN A_B
TIME_A EVENT_A TIME_B EVENT_B
4 a0 2 b2
4 a0 3 b3
4 a0 4 b4
8 a0 6 b5
8 a0 7 b6
8 a0 8 b7
Query 1:
If you are not going to have overlapping ranges then you can use:
SELECT *
FROM (
SELECT TIME_B,
EVENT_B,
MAX( TIME_A ) OVER ( ORDER BY TIME_B ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING )
AS TIME_A,
MAX( EVENT_A ) OVER ( ORDER BY TIME_B ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING )
AS EVENT_A
FROM tableB B
LEFT OUTER JOIN tableA A
ON ( B.TIME_B = A.TIME_A AND A.EVENT_A = 'a0' )
)
WHERE TIME_A IS NOT NULL;
Which only uses a single join and then finds the valid rows with analytic functions.
Output:
TIME_B | EVENT_B | TIME_A | EVENT_A
-----: | :------ | -----: | :------
2 | b2 | 4 | a0
3 | b3 | 4 | a0
4 | b4 | 4 | a0
6 | b5 | 8 | a0
7 | b6 | 8 | a0
8 | b7 | 8 | a0
db<>fiddle here
Query 2:
If you could have overlapping ranges then you could use a hierarchical query to generate the rows:
SELECT TIME_B,
EVENT_B,
CONNECT_BY_ROOT( TIME_A ) AS TIME_A,
CONNECT_BY_ROOT( EVENT_A ) AS EVENT_A
FROM (
SELECT A.*,
B.*,
ROW_NUMBER() OVER ( ORDER BY TIME_B ) AS rn
FROM tableB B
LEFT OUTER JOIN tableA A
ON ( B.TIME_B = A.TIME_A AND A.EVENT_A = 'a0' )
)
WHERE LEVEL <= 2
START WITH EVENT_A IS NOT NULL
CONNECT BY PRIOR rn -2 <= rn AND rn < PRIOR rn
ORDER BY time_a, time_b
Output:
TIME_B | EVENT_B | TIME_A | EVENT_A
-----: | :------ | -----: | :------
2 | b2 | 4 | a0
3 | b3 | 4 | a0
4 | b4 | 4 | a0
6 | b5 | 8 | a0
7 | b6 | 8 | a0
8 | b7 | 8 | a0
8 | b7 | 10 | a0
9 | b8 | 10 | a0
10 | b9 | 10 | a0
db<>fiddle here
This can be achieved using a simple join. No need to use any functions.
Try the following code, if TIME_A and TIME_B are continuous:
WITH tableA ( TIME_A, EVENT_A ) AS
(SELECT 1, 'a1' FROM DUAL UNION ALL
SELECT 2, 'a1' FROM DUAL UNION ALL
SELECT 3, 'a1' FROM DUAL UNION ALL
SELECT 4, 'a0' FROM DUAL UNION ALL
SELECT 5, 'a2' FROM DUAL UNION ALL
SELECT 6, 'a2' FROM DUAL UNION ALL
SELECT 7, 'a3' FROM DUAL UNION ALL
SELECT 8, 'a0' FROM DUAL),
tableB ( TIME_B, EVENT_B ) AS
(SELECT 1, 'b1' FROM DUAL UNION ALL
SELECT 2, 'b2' FROM DUAL UNION ALL
SELECT 3, 'b3' FROM DUAL UNION ALL
SELECT 4, 'b4' FROM DUAL UNION ALL
SELECT 5, 'b5' FROM DUAL UNION ALL
SELECT 6, 'b5' FROM DUAL UNION ALL
SELECT 7, 'b6' FROM DUAL UNION ALL
SELECT 8, 'b7' FROM DUAL)
SELECT
TIME_A,
EVENT_A,
TIME_B,
EVENT_B
FROM
TABLEA A
JOIN TABLEB B ON ( EVENT_A = 'a0'
AND TIME_B BETWEEN TIME_A - 2 AND TIME_A )
ORDER BY
TIME_A,
TIME_B
Try the following code, if TIME_A and TIME_B are not continuous:
WITH tableA ( TIME_A, EVENT_A ) AS
(SELECT 1, 'a1' FROM DUAL UNION ALL
SELECT 2, 'a1' FROM DUAL UNION ALL
SELECT 3, 'a1' FROM DUAL UNION ALL
SELECT 4, 'a0' FROM DUAL UNION ALL
SELECT 5, 'a2' FROM DUAL UNION ALL
SELECT 6, 'a2' FROM DUAL UNION ALL
SELECT 7, 'a3' FROM DUAL UNION ALL
SELECT 8, 'a0' FROM DUAL),
tableB ( TIME_B, EVENT_B ) AS
(SELECT 1, 'b1' FROM DUAL UNION ALL
SELECT 2, 'b2' FROM DUAL UNION ALL
SELECT 3, 'b3' FROM DUAL UNION ALL
SELECT 4, 'b4' FROM DUAL UNION ALL
SELECT 5, 'b5' FROM DUAL UNION ALL
SELECT 6, 'b5' FROM DUAL UNION ALL
SELECT 7, 'b6' FROM DUAL UNION ALL
SELECT 8, 'b7' FROM DUAL)
SELECT
TIME_A,
EVENT_A,
TIME_B,
EVENT_B FROM
(SELECT
TIME_A,
EVENT_A,
TIME_B,
EVENT_B,
ROW_NUMBER() OVER (PARTITION BY TIME_A ORDER BY TIME_B DESC NULLS LAST) AS RN
FROM
TABLEA A
JOIN TABLEB B ON ( EVENT_A = 'a0'
AND TIME_B <= TIME_A ))
WHERE RN <= 3
ORDER BY
TIME_A,
TIME_B
DB Fiddle demo
Cheers!!

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;

Joining on a match with at least one argument

I have a two tables:
T1(RES1, RES2)
T2(RES, WORD, COUNT)
I need to generate T3:
T3(RES1, RES2, WORD, COUNT)
As in the following example
T1
RES1 RES2
------------
A B
C D
T2
RES WORD COUNT
----------------------
A W1 10
B W1 5
B W2 7
C W2 8
T3
RES1 RES2 WORD COUNT
-----------------------------
A B W1 15 = (10+5)
A B W2 7 = (NOTHING FOR A+7)
C D W2 8 = (8+NOTHING FOR D)
That is, for every pair in T1, generate the sum distinct word counts that occur with it. What would be the most efficient way to do this in SQL?
-- sample of data from your question
SQL> with t1(RES1, RES2) as(
2 select 'A', 'B' from dual union all
3 select 'C', 'D' from dual
4 ),
5 t2(RES , WORD, COUNT1) as(
6 select 'A', 'W1', 10 from dual union all
7 select 'B', 'W1', 5 from dual union all
8 select 'B', 'W2', 7 from dual union all
9 select 'C', 'W2', 8 from dual
10 ) -- the query
11 select t1.res1
12 , t1.res2
13 , t2.word
14 , sum(t2.count1) as count
15 from t1
16 join t2
17 on (t1.res1 = t2.res or
18 t1.res2 = t2.res)
19 group by t1.res1
20 , t1.res2
21 , t2.word
22 order by t1.res1
23 ;
RES1 RES2 WORD COUNT
---- ---- ---- ----------
A B W1 15
A B W2 7
C D W2 8