从查询中消除NOT IN

时间:2018-02-05 21:36:21

标签: sql oracle

我正在尝试消除在查询中使用NOT IN的需要:

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)

原因:我读到NOT IN很慢(+ 500k行)并且不使用索引

我试过了:

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

3 个答案:

答案 0 :(得分:5)

NOT IN确实使用索引,至少在Oracle等主管数据库中使用索引。但是,如果您愿意,可以使用join来编写此内容。

但是,为什么这不符合你的要求呢?

select count(*) 
FROM TABLE1 T1
WHERE T1.ownerid = -2000 AND T1.SUBTYPE = 144;

您使用的是LEFT JOIN,因此唯一的区别是您的版本会计算TABLE2中的重复项。但这可能并不适用。

您的查询确实没有意义,因为您要将T1.dataidT1.dataid进行比较。但是,与Table3的比较对结果没有影响。所以,你可以删除它:

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 JOINON子句中的过滤不会删除任何行。因为它是NOT IN,所以不可能有重复。

答案 1 :(得分:1)

使用WHERE x IS NULL过滤器模拟NOT IN

SQL Fiddle

Oracle 11g R2架构设置

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
;

阅读设置中的注释,以查看我如何构建数据。它非常简单,数不多,但它可以让您了解这些数据如何协同工作。

<强>查询

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) |

优化器通常使用WHERE x IS NULL语法运行良好,索引仍应适用,但如果Oracle能够使用NOT IN中的索引,那么这是一个很大的优点。如果您正在处理大量数据,IS NULL方法可以快得多。最好的检查是用你的实际数据进行测试。

答案 2 :(得分:0)

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)