Почему планировщик запросов Oracle добавляет предикат фильтра, который реплицирует ограничение? - PullRequest
0 голосов
/ 08 февраля 2019

У меня есть простой запрос Oracle с планом, который не имеет смысла.

SELECT
    u.custid AS "custid",
    l.listid AS "listid"
FROM
    users u
    INNER JOIN lists l ON u.custid = l.custid

И вот что объясняет автоматическая трассировка, говорит, что у него есть план

------------------------------------------------------------------------------------------
| Id  | Operation             | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |          |  1468K|    29M|       | 11548   (1)| 00:00:01 |
|*  1 |  HASH JOIN            |          |  1468K|    29M|  7104K| 11548   (1)| 00:00:01 |
|   2 |   INDEX FAST FULL SCAN| USERS_PK |   404K|  2367K|       |   266   (2)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL   | LISTS    |  1416K|    20M|       |  9110   (1)| 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)
   - this is an adaptive plan
   - 1 Sql Plan Directive used for this statement

Меня беспокоит предикат 3. sort_type не появляется в запросе и не индексируется вообще.Мне кажется, что sort_type вообще не должен участвовать в этом запросе.

Там означает ограничение на lists.sort_type: (Да, я знаю, что мы могли бы иметь sort_typeбыть целым, а не числом)

sort_type   NUMBER DEFAULT 2 NOT NULL,
    CONSTRAINT lists_sort_type CHECK ( sort_type BETWEEN 1 AND 16 AND TRUNC(sort_type) = sort_type )

Мне кажется, что этот фильтр включен sort_type - это, по сути, тавтология.Каждая строка в lists должна проходить этот фильтр из-за этого ограничения.

Если я удаляю ограничение, фильтр больше не отображается в плане, и оценочная стоимость немного снижается.Если я добавлю ограничение обратно, план снова использует фильтр.Нет существенной разницы в скорости выполнения, так или иначе.

Я обеспокоен тем, что обнаружил этот фильтр в гораздо более крупном и сложном запросе, который я пытался оптимизировать по сравнению с несколькими минутами выполнения..

Почему Oracle добавляет этот фильтр, и это проблема и / или указывает на другую проблему?

РЕДАКТИРОВАТЬ: Если я изменяю ограничение на sort_type, чтобы не иметь TRUNC часть, фильтр исчезает.Если я разделю ограничение на два разных ограничения, фильтр вернется.

1 Ответ

0 голосов
/ 09 февраля 2019

Вообще говоря, 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 может создавать расширения статистики самостоятельно.Так что там могут быть расширенные характеристики, о которых вы даже не подозревали.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...