作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
听说OR
s 不好,并且有多个 OR
s 可能会显着影响性能。但是行独立的情况如何OR
?看一个例子:
SELECT
*
FROM
some_table t
WHERE
(
some_function('CONTEXT') = 'context of selecting by id'
AND t.id = TO_NUMBER(another_function('ID'))
)
OR (
some_function('CONTEXT') = 'context of filtering by name'
AND t.name LIKE '%' || another_function('NAME') || '%'
)
OR (
some_function('CONTEXT') = 'context of taking actual rows'
AND TO_DATE(another_function('ACTUAL_DATE'), '...')
BETWEEN t.start_date AND t.end_date
)
...
some_function('CONTEXT')
无论行如何,都返回相同的值(它不使用任何与行相关的数据,例如列值作为其参数,并且在查询执行时它不会改变影响结果的内部状态)。它也可以只是一个包变量,如
some_package.context
.
some_function('CONTEXT')
先决定哪个
OR
采取。
最佳答案
您需要使用未记录的提示 use_concat(or_predicates(1))
或使用 UNION ALL
重写查询.无论函数如何,优化器在处理这些类型的谓词时都存在问题。
预期计划
你想要一个看起来像这样的计划:
------------------------------------------------------
| Id | Operation | Name |
------------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | CONCATENATION | |
|* 2 | FILTER | |
|* 3 | TABLE ACCESS FULL | SOME_TABLE |
|* 4 | FILTER | |
|* 5 | TABLE ACCESS FULL | SOME_TABLE |
|* 6 | FILTER | |
|* 7 | TABLE ACCESS BY INDEX ROWID| SOME_TABLE |
|* 8 | INDEX UNIQUE SCAN | SYS_C0010268 |
------------------------------------------------------
FILTER
在
Operation
与典型的
filter
大不相同在
Predicate Information
解释计划的部分。这些
FILTER
将
AND
s 和
OR
s,可能会有
FILTER
.
----------------------------------------
| Id | Operation | Name |
----------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | TABLE ACCESS FULL| SOME_TABLE |
----------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SOME_FUNCTION"('CONTEXT')='context of filtering by name'
AND "T"."NAME" LIKE '%'||"ANOTHER_FUNCTION"('NAME')||'%' OR
"SOME_FUNCTION"('CONTEXT')='context of taking actual rows' AND
"T"."START_DATE"<=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...') AND
"T"."END_DATE">=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...') OR
"SOME_FUNCTION"('CONTEXT')='context of selecting by id' AND
"T"."ID"=TO_NUMBER("ANOTHER_FUNCTION"('ID')))
drop table some_table purge;
create table some_table
(
id number primary key,
name varchar2(100),
start_date date,
end_date date
);
begin
for i in 1 .. 10 loop
insert into some_table
select
level+(i*100000),
'Name '||mod(level, 5),
date '2000-01-01' + mod(level, 10000),
date '2010-01-01' + mod(level, 10000)
from dual
connect by level <= 100000;
end loop;
end;
/
begin
dbms_stats.gather_table_stats(user, 'SOME_TABLE'
,method_opt => 'for all columns size 254');
end;
/
some_function
以永远不会匹配任何东西的方式。
--Static functions.
create or replace function some_function(p_context in varchar2) return varchar2 is
begin
return p_context;
end;
/
--Btw, returning stringly-typed data is almost always a horrible idea.
--(Althogh if you're dealing with sys_context you may not have a choice.)
create or replace function another_function(p_type in varchar2) return varchar2 is
begin
if p_type = 'ID' then
return '1';
elsif p_type = 'NAME' then
return 'Name 1';
elsif p_type = 'ACTUAL_DATE' then
return '2000-01-01';
end if;
end;
/
explain plan for
SELECT * FROM some_table t
WHERE
(
some_function('CONTEXT') = 'context of selecting by id'
AND t.id = TO_NUMBER(another_function('ID'))
)
OR (
some_function('CONTEXT') = 'context of filtering by name'
AND t.name LIKE '%' || another_function('NAME') || '%'
)
OR (
some_function('CONTEXT') = 'context of taking actual rows'
AND TO_DATE(another_function('ACTUAL_DATE'), '...')
BETWEEN t.start_date AND t.end_date
);
select * from table(dbms_xplan.display);
Plan hash value: 3038250352
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 525 | 14700 | 1504 (17)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| SOME_TABLE | 525 | 14700 | 1504 (17)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SOME_FUNCTION"('CONTEXT')='context of filtering by name'
AND "T"."NAME" LIKE '%'||"ANOTHER_FUNCTION"('NAME')||'%' OR
"SOME_FUNCTION"('CONTEXT')='context of taking actual rows' AND
"T"."START_DATE"<=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...') AND
"T"."END_DATE">=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...') OR
"SOME_FUNCTION"('CONTEXT')='context of selecting by id' AND
"T"."ID"=TO_NUMBER("ANOTHER_FUNCTION"('ID')))
USE_CONCAT
提示会将查询转换为单独的
UNION ALL
脚步。
FILTER
手术。不幸的是
USE_CONCAT
有一些奇怪的限制。有时它只有在索引时才有效
or_predicates(1)
使它工作,但它完全没有记录。
explain plan for
SELECT --+ use_concat(or_predicates(1))
*
FROM some_table t
WHERE
(
some_function('CONTEXT') = 'context of selecting by id'
AND t.id = TO_NUMBER(another_function('ID'))
)
OR (
some_function('CONTEXT') = 'context of filtering by name'
AND t.name LIKE '%' || another_function('NAME') || '%'
)
OR (
some_function('CONTEXT') = 'context of taking actual rows'
AND TO_DATE(another_function('ACTUAL_DATE'), '...')
BETWEEN t.start_date AND t.end_date
);
select * from table(dbms_xplan.display);
Plan hash value: 1618041905
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 52500 | 1435K| 2721 (8)| 00:00:01 |
| 1 | CONCATENATION | | | | | |
|* 2 | FILTER | | | | | |
|* 3 | TABLE ACCESS FULL | SOME_TABLE | 2500 | 70000 | 1362 (8)| 00:00:01 |
|* 4 | FILTER | | | | | |
|* 5 | TABLE ACCESS FULL | SOME_TABLE | 49999 | 1367K| 1356 (7)| 00:00:01 |
|* 6 | FILTER | | | | | |
|* 7 | TABLE ACCESS BY INDEX ROWID| SOME_TABLE | 1 | 28 | 3 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | SYS_C0010269 | 1 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("SOME_FUNCTION"('CONTEXT')='context of taking actual rows')
3 - filter("T"."START_DATE"<=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...') AND
"T"."END_DATE">=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...'))
4 - filter("SOME_FUNCTION"('CONTEXT')='context of filtering by name')
5 - filter("T"."NAME" LIKE '%'||"ANOTHER_FUNCTION"('NAME')||'%' AND
(LNNVL("SOME_FUNCTION"('CONTEXT')='context of taking actual rows') OR
LNNVL("T"."START_DATE"<=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...')) OR
LNNVL("T"."END_DATE">=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...'))))
6 - filter("SOME_FUNCTION"('CONTEXT')='context of selecting by id')
7 - filter((LNNVL("SOME_FUNCTION"('CONTEXT')='context of filtering by name') OR
LNNVL("T"."NAME" LIKE '%'||"ANOTHER_FUNCTION"('NAME')||'%')) AND
(LNNVL("SOME_FUNCTION"('CONTEXT')='context of taking actual rows') OR
LNNVL("T"."START_DATE"<=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...')) OR
LNNVL("T"."END_DATE">=TO_DATE("ANOTHER_FUNCTION"('ACTUAL_DATE'),'...'))))
8 - access("T"."ID"=TO_NUMBER("ANOTHER_FUNCTION"('ID')))
explain plan for
SELECT * FROM some_table t
WHERE some_function('CONTEXT') = 'context of selecting by id' AND t.id = TO_NUMBER(another_function('ID'))
union all
SELECT * FROM some_table t
WHERE some_function('CONTEXT') = 'context of filtering by name' AND t.name LIKE '%' || another_function('NAME') || '%'
union all
SELECT * FROM some_table t
WHERE some_function('CONTEXT') = 'context of taking actual rows' AND TO_DATE(another_function('ACTUAL_DATE'), '...') BETWEEN t.start_date AND t.end_date
select * from table(dbms_xplan.display);
(Plan not shown - it's basically the same as the `USE_CONCAT` version.)
CASE
是个好主意,但在这里似乎行不通。虽然这可能只是我的具体例子的问题。
explain plan for
SELECT *
FROM some_table t
WHERE
case
when some_function('CONTEXT') = 'context of selecting by id'
AND t.id = TO_NUMBER(another_function('ID')) then 1
when some_function('CONTEXT') = 'context of filtering by name'
AND t.name LIKE '%' || another_function('NAME') || '%' then 1
when some_function('CONTEXT') = 'context of taking actual rows'
AND TO_DATE(another_function('ACTUAL_DATE'), '...') BETWEEN t.start_date AND t.end_date then 1
else 0 end
= 1;
select * from table(dbms_xplan.display);
(Plan not shown - it's basically the same as the default version with the full table scan.)
关于sql - Oracle SQL 优化器在处理 OR 和与行无关的谓词时的行为(例如,无论行如何,函数都返回相同的值),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18205955/
我是一名优秀的程序员,十分优秀!