左连接并排除某些比赛中不存在的行

时间:2014-12-09 14:08:26

标签: sql oracle join

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

我想在TABLE2中查询(PROP1, PROP2)元组中缺少的NUM值,例如(a,b,3)元组。但是,如果TABLE2中不存在元组,例如(a,b)。我不想在结果中退回。

所以,我的预期输出是

PROP1   PROP2   NUM
a       a       3

在我写完的查询之后,还返回了(a,b)元组,这是我不想要的。

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

我想要排除这3行,所以我再次加入TABLE2并将结果分组,这样可以得到正确的结果。

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

问题:如果没有GROUP BY语句,我有什么方法可以做到这一点,因为它对于大型表来说有点穷尽。

我使用的是Oracle 11g。

3 个答案:

答案 0 :(得分:1)

这会做你想要的,但我不知道它是否会更有效:

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;

首先在匹配的行上过滤table1,然后进行最终比较。

答案 1 :(得分:1)

我就是这样做的

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

另一种方法是:

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)

编辑: 我尝试使用exists来获取它,只使用table2一次,但我没有找到解决方案,如果其他人找到了解决方案,我会被强迫。

答案 2 :(得分:1)

根据Gordon对性能的回答,IS NULL可能是性能问题。

IS NULL会抑制index的使用。因为NULL值未编入索引。

有两种方法可以使用IS NULL索引:

1。BITMAP索引。但是,更适用于OLTP系统。

2.我最喜欢的方式,很高兴展示。我们可以制作 leaves of the b-tree index a constant 。因此,在查询NULL时使用索引。基本上,NULL都在索引的顶部/底部。 Oracle可以使用索引forwards or backwards,因此并不重要。它会执行 full scan of the index

我在这里回答了类似的问题http://www.orafaq.com/forum/mv/msg/194746/625371/#msg_625371

由于OR为空条件,第一种情况不会使用索引:

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

让叶子保持不变:

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>

我在这里回答了类似的问题Oracle. Select all if parameter is null else return specific item issue