在 sql 查询中使用 not in 时优化此 SQL 语句的最佳方法是啥?

Posted

技术标签:

【中文标题】在 sql 查询中使用 not in 时优化此 SQL 语句的最佳方法是啥?【英文标题】:Is it the best way to optimize this SQL statement when using not in in sql query?在 sql 查询中使用 not in 时优化此 SQL 语句的最佳方法是什么? 【发布时间】:2020-06-10 16:13:17 【问题描述】:

名为TB_ORDER的表有9000万条数据记录,但只有500条STATE既不是B也不是C的记录。

SELECT 
    ORDER.ID,ORDER.STATE,ORDER.NAME 
FROM 
    TB_ORDER ORDER 
WHERE 
    ORDER.STATE NOT IN ('B','C') ; 

我的同事这样写sql,因为full table scan,它花费了大约7分钟。所以我尝试像这样更改它。没关系吗?我在状态字段上添加了索引。还是full table scan是因为子查询sql结果很大((90000000-500)/90000000)吗?

SELECT 
    A.ID,A.NAME,A.STATE 
FROM TB_ORDER A 
WHERE 
    NOT EXISTS
        (
        SELECT 1 FROM TB_ORDER B WHERE A.ID=B.ID and B.STATE='B'
        UNION ALL
        SELECT 1 FROM TB_ORDER C WHERE A.ID=C.ID and C.STATE='C'
        )

【问题讨论】:

对我来说看起来不错。他们给出相同的结果吗?子选择几乎总是比不慢。如果可以,请避开它​​们。 从 9000 万条记录中仅排除 500 条记录总是需要时间。您可能会尝试对表进行分区。 @AnkitBajpai,我想要的真正结果是总结果中只有 500 条记录。 第一个版本很好。需要进行全表扫描。 @GordonLinoff,不,它运行这么慢,所以我的老板让我优化它。 【参考方案1】:

你真的需要 NOT IN 吗?您可以通过使用函数然后创建基于函数的索引来解决此问题。确保您的 where 子句与谓词完全匹配。示例:

-- table
create table t_large_table (id NUMBER GENERATED ALWAYS AS IDENTITY,state VARCHAR2(1));
-- some sample data
DECLARE
BEGIN
  FOR i IN 1 .. 10 LOOP
    INSERT INTO t_large_table (state) VALUES ('A');
    INSERT INTO t_large_table (state) VALUES ('B');
  END LOOP;
  INSERT INTO t_large_table (state) VALUES ('C');
  INSERT INTO t_large_table (state) VALUES ('D');
  COMMIT;
END;
/

-- create index with function that has a bucket to put all states that are relevant to me. In this case everything that is not A or B
CREATE INDEX t_large_table_idx 
  ON t_large_table (CASE state WHEN 'A' THEN 'A' WHEN 'B' THEN 'B' ELSE 'X' END);

-- run a select with exactly same function as the index
SELECT * 
  FROM t_large_table
 WHERE CASE state WHEN 'A' THEN 'A' WHEN 'B' THEN 'B' ELSE 'X' END = 'X';

-- check explain plan
-----------------------------------------------------------------
| Id  | Operation                           | Name              |
-----------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                   |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T_LARGE_TABLE     |
|   2 |   INDEX RANGE SCAN                  | T_LARGE_TABLE_IDX |
-----------------------------------------------------------------

【讨论】:

【参考方案2】:

我提个建议,你可以试试。

Select o.ID,o.o,o.NAME 
FROM TB_ORDER o
Inner Join
(
 Select STATE  from
 (
  Select STATE from TB_ORDER Group by ORDER 
 ) Q Where STATE NOT IN ('B','C') 
) QQ on QQ.STATE = o.STATE

【讨论】:

以上是关于在 sql 查询中使用 not in 时优化此 SQL 语句的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Sql server not in优化

使用 JOIN 而不是 NOT IN 优化 SQL 查询

使用 NOT IN(Oracle Sql Developer)的查询性能优化

SQL优化 - 避免使用 IN 和 NOT IN

Oracle学习篇之SQL语句的优化

sql优化,in与exist , not in与not exist 的区别