在 sql 查询中使用 not in 时,这是优化此 SQL 语句的最佳方法吗?
Is it the best way to optimize this SQL statement when using not in in sql query?
叫TB_ORDER
的table有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 分钟。所以我试着把它改成 this.Its 这样好吗?我已经添加了索引状态字段。还是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'
)
你真的需要 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 |
-----------------------------------------------------------------
我给个建议,你可以试试
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
叫TB_ORDER
的table有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 分钟。所以我试着把它改成 this.Its 这样好吗?我已经添加了索引状态字段。还是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'
)
你真的需要 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 |
-----------------------------------------------------------------
我给个建议,你可以试试
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