Вообще говоря, Oracle генерирует предикаты, основываясь на ваших ограничениях CHECK
, всякий раз, когда это дает оптимизатору больше информации для составления хорошего плана.Это не всегда достаточно умно, чтобы распознать, когда они излишни.Вот краткий пример в Oracle 12c с использованием имен ваших таблиц:
-- Create the CUSTS table
CREATE TABLE custs ( custid number not null );
CREATE INDEX custs_u1 on custs (custid);
-- Create the LISTS table
CREATE TABLE lists
( listid number not null,
sort_type number not null,
custid number,
constraint lists_c1 check ( sort_type between 1 and 16 and
trunc(sort_type) = sort_Type )
);
-- Explain a join
EXPLAIN PLAN FOR
SELECT /*+ USE_HASH(u) */
u.custid AS "custid",
l.listid AS "listid"
FROM custs u
INNER JOIN lists l ON u.custid = l.custid;
-- Show the plan
select * from table(dbms_xplan.display);
Plan hash value: 2443150416
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 39 | 3 (0)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 39 | 3 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN | CUSTS_U1 | 1 | 13 | 1 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| LISTS | 1 | 26 | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("U"."CUSTID"="L"."CUSTID")
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
Пока ничего странного.Не добавлено сомнительных предикатов.
Теперь давайте скажем оптимизатору Oracle, что распределение данных на TRUNC(sort_type)
может иметь значение ...
declare
x varchar2(30);
begin
x := dbms_stats.create_extended_stats ( user, 'LISTS', '(TRUNC(SORT_TYPE))');
dbms_output.put_line('Extension name = ' || x);
end;
... и, теперь, давайте объяснимтот же запрос снова ...
-- Re-explain the same join as before
EXPLAIN PLAN FOR
SELECT /*+ USE_HASH(u) */
u.custid AS "custid",
l.listid AS "listid"
FROM custs u
INNER JOIN lists l ON u.custid = l.custid;
-- Show the new plan
select * from table(dbms_xplan.display);
Plan hash value: 2443150416
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 52 | 3 (0)| 00:00:01 |
|* 1 | HASH JOIN | | 1 | 52 | 3 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN | CUSTS_U1 | 1 | 13 | 1 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| LISTS | 1 | 39 | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("U"."CUSTID"="L"."CUSTID")
3 - filter(TRUNC("SORT_TYPE")>=1 AND TRUNC("SORT_TYPE")<=16)
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
Теперь Oracle добавил предикат, потому что CBO может извлечь из этого пользу.Это действительно извлекает выгоду из этого?Нет, но Oracle достаточно умен, чтобы знать, что он может и что он (*) ничего не ранит.
(*) в предыдущих версиях было множество ошибок, из-за которых это наносило вред, мешая избирательности, оцениваемой CBO.
Наличие расширенной статистикитолько одна примерная причина того, почему Oracle может подумать, что может извлечь выгоду из этого предиката.Чтобы выяснить, является ли это причиной в вашем случае, вы можете посмотреть расширенную статистику в вашей базе данных следующим образом:
SELECT * FROM dba_stat_extensions where table_name = 'LISTS';
Имейте в виду, что Oracle CBO может создавать расширения статистики самостоятельно.Так что там могут быть расширенные характеристики, о которых вы даже не подозревали.