Yigitalp Ertem
Yigitalp Ertem

Reputation: 2039

Left join and excluding non-existent rows on certain matchups

fiddle.

TABLE1
PROP1   PROP2   NUM
a       a       1
a       a       2
a       a       3
a       b       1
a       b       2
a       b       3

TABLE2
PROP1   PROP2   NUM
a       a       1
a       a       2

I want to query the missing NUM values in TABLE2 with regards to (PROP1, PROP2) tuples such as (a,b,3). However, if a tuple does not exist in TABLE2 such as (a,b). I don't want to return it in the result.

So, my expected output is

PROP1   PROP2   NUM
a       a       3

Following query I wrote, returns the (a,b) tuples as well, which I don't want to.

SELECT *
FROM TABLE1 T1
LEFT JOIN TABLE2 T2 ON T1.PROP1 = T2.PROP1 AND T1.PROP2 = T2.PROP2 AND T1.NUM = T2.NUM
WHERE T2.NUM IS NULL

I want to exclude these 3 rows, so I join with TABLE2 one more time and group the results which gives me the correct result.

SELECT T1.PROP1, T1.PROP2, T1.NUM
FROM TABLE1 T1
LEFT JOIN TABLE2 T2 ON T1.PROP1 = T2.PROP1 AND T1.PROP2 = T2.PROP2 AND T1.NUM = T2.NUM
JOIN TABLE2 T22 ON T1.PROP1 = T22.PROP1 AND T1.PROP2 = T22.PROP2
WHERE T2.NUM IS NULL
GROUP BY T1.PROP1, T1.PROP2, T1.NUM

Question: Is there any way I can do it without a GROUP BY statement since it is a bit exhaustive for large tables.

I'm using Oracle 11g.

Upvotes: 1

Views: 65

Answers (3)

Gordon Linoff
Gordon Linoff

Reputation: 1270573

This will do what you want, but I don't know if it will be more efficient:

SELECT *
FROM TABLE1 T1 JOIN
     (SELECT DISTINCT PROP1, PROP2
      FROM TABLE2
     ) TT2
     ON T1.PROP1 = TT2.PROP1 AND t1.PROP2 = TT2.PROP2 LEFT JOIN
     TABLE2 T2
     ON T1.PROP1 = T2.PROP1 AND T1.PROP2 = T2.PROP2 AND T1.NUM = T2.NUM
WHERE T2.NUM IS NULL;

It first filters table1 on the matching rows, and then does the final comparison.

Upvotes: 1

a.j. tawleed
a.j. tawleed

Reputation: 904

That's how I would have done it

with table1 as
(select 'a' prop1, 'a' prop2, 1 num from dual
union all
select 'a' , 'a' , 2  from dual
union all
select 'a' , 'a' , 3  from dual
union all
select 'a' , 'b' , 1  from dual
union all
select 'a' , 'b' , 2  from dual
union all
select 'a' , 'b' , 3 from dual),
table2 as(
select 'a' prop1, 'a' prop2, 1 num from dual
union all
select 'a' , 'a' , 2  from dual
)
select prop1, prop2, num
from table1
where (prop1, prop2) in (select prop1, prop2 from table2)
minus
select prop1, prop2, num
from table2

another approach is this:

select prop1, prop2, num
from table1
where (prop1, prop2, num) not in(select prop1, prop2, num
from table2)
and (prop1, prop2) in (select prop1, prop2 from table2)

Edit: I tried playing around with exists to get it, to use table2 just once, but I don't find a solution for that, I would be interessted if someone else finds a solution for that.

Upvotes: 1

Lalit Kumar B
Lalit Kumar B

Reputation: 49092

Based on Gordon's answer about the performance, the IS NULL might be a performance issue.

IS NULL suppresses the index usage. Because NULL values are not indexed.

There are two ways to make the use of index with IS NULL :

1.BITMAP index. However, more applicable in OLTP systems.

2.My favourite way, and nice to demonstrate. We could make the leaves of the b-tree index a constant. Thus, making use of index while querying for NULL. Basically, the NULLs are all together, at the top/bottom of the index. Oracle can use the index forwards or backwards, so doesn't really matter. And it does a full scan of the index.

I have answered a similar question here http://www.orafaq.com/forum/mv/msg/194746/625371/#msg_625371

The first scenario won't use the index due to the OR is null condition :

SQL> SELECT * FROM PROD_NEW;

PROFILE_TYPE
---------------

Prod
Prodparallel

Prod

SQL> CREATE INDEX PROD_NEW_I1 ON PROD_NEW
  2    (PROFILE_TYPE
  3    );

Index created.

SQL> EXPLAIN PLAN FOR SELECT * FROM PROD_NEW WHERE PROFILE_TYPE = 'Prod' OR PROFILE_TYPE IS NULL;

Explained.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2121244107

------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |     3 |    15 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| PROD_NEW |     3 |    15 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

   1 - filter("PROFILE_TYPE" IS NULL OR "PROFILE_TYPE"='Prod')

13 rows selected

Let's make the leaves constant :

SQL> DROP INDEX PROD_NEW_I1;

Index dropped.

SQL> CREATE INDEX PROD_NEW_I1 ON PROD_NEW
  2    (PROFILE_TYPE,1
  3    );

Index created.

SQL> EXPLAIN PLAN FOR SELECT * FROM PROD_NEW WHERE PROFILE_TYPE = 'Prod' OR PROFILE_TYPE IS NULL;

Explained.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1272076902

--------------------------------------------------------------------------------
| Id  | Operation        | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |             |     3 |    15 |     1   (0)| 00:00:01 |
|*  1 |  INDEX FULL SCAN | PROD_NEW_I1 |     3 |    15 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

   1 - filter("PROFILE_TYPE" IS NULL OR "PROFILE_TYPE"='Prod')

13 rows selected.

SQL>

I answered to a similar question here, Oracle. Select all if parameter is null else return specific item issue

Upvotes: 1

Related Questions