我的大表具有相同的基于函数的索引。查询一个表时,查询计划使用索引。当我用union all加入第二个表时,我有完整的扫描。
我找到了一个有效的解决方案(WORKING JOIN WITH TABLE),但它有一些限制:
如何克服上述限制?
我尝试了像CARDINALITY这样的提示,告诉记录的数量很少,有些重写(REWRITE,PUSH_PRED)没有成功。 我不能使用分区,因为问题出在Oracle SE上。
此处显示的问题是我的实际问题的简化:
实际上,我可能会有许多不同的表格进行adhoc查询和连接。因此,我不能简单地将连接推送到一个最简单的解决方案。
-- FULL SCAN FOR IN SUBQUERY
SELECT * FROM (
SELECT * FROM TEST_EXPV1
UNION ALL SELECT * FROM TEST_EXPV2
)
WHERE DECODE(Value, -1, CAST(NULL AS NUMBER(38)), 0, CAST(NULL AS NUMBER(38)), Value) IN (SELECT Id FROM test_10r)
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200K| 5078K| 916 (3)| 00:00:11 |
|* 1 | HASH JOIN | | 200K| 5078K| 916 (3)| 00:00:11 |
| 2 | VIEW | VW_NSO_1 | 10 | 130 | 4 (25)| 00:00:01 |
| 3 | HASH UNIQUE | | 10 | 30 | 4 (25)| 00:00:01 |
| 4 | TABLE ACCESS FULL| TEST_10R | 10 | 30 | 3 (0)| 00:00:01 |
| 5 | VIEW | | 2000K| 24M| 902 (2)| 00:00:11 |
| 6 | UNION-ALL | | | | | |
| 7 | TABLE ACCESS FULL| TEST_EXPV1 | 1000K| 3906K| 451 (2)| 00:00:06 |
| 8 | TABLE ACCESS FULL| TEST_EXPV2 | 1000K| 3906K| 451 (2)| 00:00:06 |
-----------------------------------------------------------------------------------
-- CORRECT RANGE INDEX SCAN for bound value
SELECT * FROM (
SELECT * FROM TEST_EXPV1
UNION ALL SELECT * FROM TEST_EXPV2
)
WHERE DECODE(Value, -1, CAST(NULL AS NUMBER(38)), 0, CAST(NULL AS NUMBER(38)), Value) = :b1001
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20000 | 253K| 979 (10)| 00:00:12 |
| 1 | VIEW | | 20000 | 253K| 979 (10)| 00:00:12 |
| 2 | UNION-ALL | | | | | |
| 3 | TABLE ACCESS BY INDEX ROWID| TEST_EXPV1 | 2 | 8 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | I_TEST_EXPV1 | 2 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| TEST_EXPV2 | 2 | 8 | 3 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | I_TEST_EXPV2 | 2 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
-- WORKING JOIN WITH TABLE
WITH x AS (SELECT Id FROM test_10r WHERE Id BETWEEN :a AND :b)
SELECT /*+ FIRST_ROWS */ * FROM (
SELECT * FROM TEST_EXPV1
UNION ALL SELECT * FROM TEST_EXPV2
) U, x
WHERE DECODE(Value, -1, CAST(NULL AS NUMBER(38)), 0, CAST(NULL AS NUMBER(38)), Value) = x.id
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 16 | 996 (11)| 00:00:12 |
|* 1 | FILTER | | | | | |
|* 2 | HASH JOIN | | 500 | 8000 | 996 (11)| 00:00:12 |
|* 3 | TABLE ACCESS FULL | TEST_10R | 10 | 30 | 3 (0)| 00:00:01 |
| 4 | VIEW | | 5000 | 65000 | 993 (11)| 00:00:12 |
| 5 | UNION-ALL | | | | | |
|* 6 | FILTER | | | | | |
| 7 | TABLE ACCESS BY INDEX ROWID| TEST_EXPV1 | 2500 | 10000 | 4192 (1)| 00:00:51 |
|* 8 | INDEX RANGE SCAN | I_TEST_EXPV1 | 4500 | | 11 (0)| 00:00:01 |
|* 9 | FILTER | | | | | |
| 10 | TABLE ACCESS BY INDEX ROWID| TEST_EXPV2 | 2500 | 10000 | 4192 (1)| 00:00:51 |
|* 11 | INDEX RANGE SCAN | I_TEST_EXPV2 | 4500 | | 11 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
答案 0 :(得分:3)
您正在对UNION ALL进行处理,它正在对两个表进行全面扫描以获得组合结果集;然后只过滤第三个表中的值。
子查询因子分解的一种更常见的模式是引用联合的每个分支中的CTE:
WITH x AS (SELECT Id FROM test_10r)
SELECT TEST_EXPV1.* FROM x JOIN TEST_EXPV1
ON DECODE(Value, -1, CAST(NULL AS NUMBER(38)), 0, CAST(NULL AS NUMBER(38)), Value) = x.Id
UNION ALL SELECT TEST_EXPV2.* FROM x JOIN TEST_EXPV2
ON DECODE(Value, -1, CAST(NULL AS NUMBER(38)), 0, CAST(NULL AS NUMBER(38)), Value) = x.Id;
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 32 | 224 | 66 (0)| 00:00:01 |
| 1 | UNION-ALL | | | | | |
| 2 | NESTED LOOPS | | 16 | 112 | 33 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | TEST_10R | 10 | 30 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| TEST_EXPV1 | 2 | 8 | 3 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | I_TEST_EXPV1 | 2 | | 1 (0)| 00:00:01 |
| 6 | NESTED LOOPS | | 16 | 112 | 33 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL | TEST_10R | 10 | 30 | 3 (0)| 00:00:01 |
| 8 | TABLE ACCESS BY INDEX ROWID| TEST_EXPV2 | 2 | 8 | 3 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN | I_TEST_EXPV2 | 2 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
答案 1 :(得分:3)
将谓词推入UNION ALL
可能是不稳定的。试试这个:
SELECT /*+ PUSH_PRED(v) */ *
FROM (SELECT * FROM test_expv1
UNION ALL
SELECT * FROM test_expv2) v INNER JOIN test_10r ON
(DECODE(Value, -1, CAST(NULL AS NUMBER(38)), 0, CAST(NULL AS NUMBER(38)), Value)) = test_10r.id;
以下是我在11.2.0.4实例中使用OP的DDL进行上述查询的结果:
SQL_ID df6dvkgjwjsq1, child number 1
-------------------------------------
SELECT /*+ PUSH_PRED(v) */ * FROM (SELECT * FROM test_expv1
UNION ALL SELECT * FROM test_expv2) v INNER JOIN test_10r ON
(DECODE(Value, -1, CAST(NULL AS NUMBER(38)), 0, CAST(NULL AS
NUMBER(38)), Value)) = test_10r.id
Plan hash value: 191389749
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 16 |00:00:00.01 | 69 |
| 1 | NESTED LOOPS | | 1 | 2000K| 16 |00:00:00.01 | 69 |
| 2 | TABLE ACCESS FULL | TEST_10R | 1 | 10 | 10 |00:00:00.01 | 22 |
| 3 | VIEW | | 10 | 32 | 16 |00:00:00.01 | 47 |
| 4 | UNION ALL PUSHED PREDICATE | | 10 | | 16 |00:00:00.01 | 47 |
| 5 | TABLE ACCESS BY INDEX ROWID| TEST_EXPV1 | 10 | 158 | 8 |00:00:00.01 | 24 |
|* 6 | INDEX RANGE SCAN | I_TEST_EXPV1 | 10 | 2 | 8 |00:00:00.01 | 16 |
| 7 | TABLE ACCESS BY INDEX ROWID| TEST_EXPV2 | 10 | 158 | 8 |00:00:00.01 | 23 |
|* 8 | INDEX RANGE SCAN | I_TEST_EXPV2 | 10 | 2 | 8 |00:00:00.01 | 15 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("TEST_EXPV1"."SYS_NC00002$"="TEST_10R"."ID")
8 - access("TEST_EXPV2"."SYS_NC00002$"="TEST_10R"."ID")