UNION
和UNION ALL
查询可以超越使用的等效查询OR
在某些情况下-连接谓词。 据我所知,这部分是因为UNION
子查询可以并行执行,因此,他们可以有自己的“子计划”具体到每一部分OR
-连接谓词,这可能是更为优化,由于简单适用的查询转换。
但写作OR
-连接谓词通常是更加易读和简洁的,即使子查询分解被应用于UNION ALL
解决方案。 我的问题是:有没有一种方法来指示甲骨文,一个单一的,昂贵的OR
-连通谓语应该转变成一个UNION ALL
操作? 如果有这样的提示/方法,在什么可以把它应用情况下(如做任何限制必须存在于参与谓词列等)? 一个例子:
CREATE TABLE a AS
SELECT 1 x, 2 y FROM DUAL UNION ALL
SELECT 2 x, 1 y FROM DUAL;
-- This query...
SELECT * FROM a
WHERE x = 1 OR y = 1
-- Is sometimes outperformed by this one, for more complex table sources...
-- Note: in my case, I can safely apply UNION ALL. I know the two predicates to
-- be mutually exclusive.
SELECT * FROM a
WHERE x = 1
UNION ALL
SELECT * FROM a
WHERE y = 1
请注意,我知道了的/*+ USE_CONCAT */
提示:
SELECT /*+ USE_CONCAT */ * FROM a
WHERE x = 1 OR y = 1
但它似乎并没有产生什么,我需要(没有强制UNION ALL
执行计划操作):
-------------------------------------------
| Id | Operation | Name | E-Rows |
-------------------------------------------
| 0 | SELECT STATEMENT | | |
|* 1 | TABLE ACCESS FULL| A | 2 |
-------------------------------------------
也许,有一些限制这个提示? 我有可用于该Oracle 11g2。
我相信,这可能是与现有您在使用列的索引OR
谓词。
我测试使用11gR2的下面。
create table scott.test as
select level l,
decode(mod(level,2), 1, 1, 2) x,
decode(mod(level,2), 1, 2, 1) y,
dbms_random.value(1, 3) z from dual
connect by level < 1000;
/
begin
dbms_stats.gather_table_stats('scott', 'test');
end;
/
然后我蟾蜍解释以下查询,( EXPLAIN PLAN FOR
)
select x, y, z from scott.test
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1)
;
SELECT STATEMENT Optimizer Mode=ALL_ROWS 10 4
TABLE ACCESS FULL COS_DM.TEST 10 280 4
select /*+ USE_CONCAT */ x, y, z from scott.test
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1)
;
SELECT STATEMENT Optimizer Mode=ALL_ROWS 10 4
TABLE ACCESS FULL COS_DM.TEST 10 280 4
select x, y, z from test where (floor(z) = 1 and x = 1)
union all
select x, y, z from test where (floor(z) = 2 and y = 1)
;
SELECT STATEMENT Optimizer Mode=ALL_ROWS 10 8
UNION-ALL
TABLE ACCESS FULL COS_DM.TEST 5 140 4
TABLE ACCESS FULL COS_DM.TEST 5 140 4
所以会出现提示不工作。 然后我添加了一个索引到X和Y列:
create index test_x on test (x, y);
begin
dbms_stats.gather_table_stats('scott', 'test');
end;
/
现在重新运行查询:
select x, y, z from scott.test
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1)
;
SELECT STATEMENT Optimizer Mode=ALL_ROWS 10 4
TABLE ACCESS FULL COS_DM.TEST 10 280 4
select /*+ USE_CONCAT */ x, y, z from scott.test
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1)
;
SELECT STATEMENT Optimizer Mode=ALL_ROWS 10 8
CONCATENATION
TABLE ACCESS FULL COS_DM.TEST 5 140 4
TABLE ACCESS FULL COS_DM.TEST 5 140 4
select x, y, z from test where (floor(z) = 1 and x = 1)
union all
select x, y, z from test where (floor(z) = 2 and y = 1)
;
SELECT STATEMENT Optimizer Mode=ALL_ROWS 10 8
UNION-ALL
TABLE ACCESS FULL COS_DM.TEST 5 140 4
TABLE ACCESS FULL COS_DM.TEST 5 140 4
看来,添加索引(即使不是正在使用 )后的优化决定毕竟使用的提示!
也许你可以试试这个?
在某些情况下,优化忽略提示,你必须写在不同的方式查询。 另一种方法重写使用UNION ALL所有查询,可以只改写条款:
SELECT * FROM a /* ... you can put here more joins with many tables ... */
WHERE a.rowid in (
select innerQry.rowid from a innerQry where /*your first clause of OR*/innerQry.x = 1
union all
select innerQry.rowid from a innerQry where /*your second clause of OR*/innerQry.y = 1
)