Luk*_*der 10 sql oracle optimization union union-all
UNION在某些情况下,UNION ALL查询可以胜过使用OR-connected谓词的等效查询.据我所知,这部分是因为子UNION选择可以并行执行,因此它们可以有自己的特定于OR连接谓词的每个部分的"子计划" ,由于更简单的适用查询转换,这可能更加优化.
但是OR,即使将子查询因子应用于UNION ALL解决方案,写连接谓词通常也更具可读性和简洁性.我的问题是:有没有办法向Oracle表明,应该将单个昂贵的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
Run Code Online (Sandbox Code Playgroud)
注意,我知道/*+ USE_CONCAT */提示:
SELECT /*+ USE_CONCAT */ * FROM a
WHERE x = 1 OR y = 1
Run Code Online (Sandbox Code Playgroud)
但它似乎没有产生我需要的东西(UNION ALL执行计划中没有强制操作):
-------------------------------------------
| Id | Operation | Name | E-Rows |
-------------------------------------------
| 0 | SELECT STATEMENT | | |
|* 1 | TABLE ACCESS FULL| A | 2 |
-------------------------------------------
Run Code Online (Sandbox Code Playgroud)
也许,这个提示有一些限制?我有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;
/
Run Code Online (Sandbox Code Playgroud)
然后我在 TOAD 中解释了以下查询,( 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
Run Code Online (Sandbox Code Playgroud)
所以看来提示不起作用。然后我向 x 和 y 列添加了一个索引:
create index test_x on test (x, y);
begin
dbms_stats.gather_table_stats('scott', 'test');
end;
/
Run Code Online (Sandbox Code Playgroud)
现在重新运行查询:
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
Run Code Online (Sandbox Code Playgroud)
看起来在添加索引后(即使它没有被使用)优化器最终决定使用提示!
也许你可以试试这个?
| 归档时间: |
|
| 查看次数: |
2429 次 |
| 最近记录: |