我得到了如下所示的SQL。 问题是表 T2 太大,下载所有数据需要数小时(显示前 7 个结果需要 500 秒)。有没有人知道如何加快"NOT IN"条款,或者用其他排除方法替换"NOT IN"?谢谢!
SELECT *
FROM T1
WHERE NVL(T1.A,'1') NOT IN (SELECT B FROM T2)
您可以使用T1
表和T2
表之间的连接来改写查询:
SELECT *
FROM T1
LEFT JOIN T2
ON NVL(T1.A, '1') = T2.B
WHERE T2.B IS NULL
如果您在连接列上有索引,特别是T2
的B
列上有索引,这可能会加快速度。
如果表T1
的记录数非常少,并且表T2
列上有索引B
则所需的访问计划是T2
T1
中对每条记录执行一次索引查找。
预期的执行计划使用嵌套循环反连接。
如果需求已满足并且优化程序统计信息是最新的,则 Oracle 将自动选择此计划 - 您无需按照其他答案中的建议重新制定查询。
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 130 | 106 (0)| 00:00:02 |
|* 1 | FILTER | | | | | |
| 2 | NESTED LOOPS ANTI SNA| | 100 | 2600 | 106 (4)| 00:00:02 |
| 3 | TABLE ACCESS FULL | T1 | 100 | 1300 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T2_IDX | 1 | 13 | 1 (0)| 00:00:01 |
|* 5 | TABLE ACCESS FULL | T2 | 4236 | 55068 | 4 (0)| 00:00:01 |
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( NOT EXISTS (SELECT 0 FROM "T2" "T2" WHERE "B" IS NULL))
4 - access("B"=NVL("T1"."A",1))
5 - filter("B" IS NULL)
这个执行计划的问题在于它随着T1
行数而线性扩展,即如果T1
很大(M 行),查询将停止执行。在这种情况下,所需的访问权限是在每个表上进行一次完全扫描。
预期的计划是哈希反联接
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 2600 | 5479 (1)| 00:01:38 |
|* 1 | HASH JOIN ANTI NA | | 100 | 2600 | 5479 (1)| 00:01:38 |
| 2 | TABLE ACCESS FULL| T1 | 100 | 1300 | 2 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 84722 | 1075K| 5476 (1)| 00:01:38 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("B"=NVL("T1"."A",1))
最后说明 - 您在条件中使用NVL(T1.A,'1')
- 表中是否存在值"1"T2
?如果这是已知的和常量,您可以简单地包含或排除T1
中的所有 NULL 值,而无需查找T2
。
祝你好运!
感谢大家的帮助。 更多背景: 1. T1 和 T2 都有索引 2. T2 的值为"1",但这不是我们想要的。我们使用NVL("T1")的原因。A",1) 是因为我们希望在最终结果的 A 列中保留空值记录。
因此,我按如下方式修改了查询,它的速度要快得多,运行和下载只需 10 分钟。
T3 为 (从 T2 中选择不同的 B)
选择 T1。
从 T1 向左加入 T1 上的 T3。A = T3.B
其中 T3.B 为空,T1。A 不为空
全部联盟
选择 T1。
其中 T1.A 为空