почему count (*) работает медленно даже с индексом? - PullRequest
0 голосов
/ 17 февраля 2020

Это мой запрос:

   select count(*)
    FROM TB_E2V_DOCUMENTOS_CICLO D
    WHERE (D.TIPOCLIENTE = null or null is null)
      AND (D.TIPODOCUMENTOCLIENTE = null or null is null)
      AND (D.NUMDOCUMENTOCLIENTE = null or null is null)
      AND (D.BA = null or null is null)
      AND (D.FA = null or null is null)
      AND (D.NOMBRECLIENTE = null or null is null)
      AND (D.NUMTELEFONO = null or null is null)
      AND (D.NUMSUSCRIPCION = null or null is null)
      AND (D.TIPORECIBO in ('Recibo'))
      AND (D.NUMRECIBO = null or null is null)
      AND (TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd') BETWEEN TO_DATE('2019-5-1', 'yyyy-MM-dd') AND TO_DATE('2020-2-18', 'yyyy-MM-dd'))
      AND (D.MONTORECIBO = null or null is null)
      AND (D.NUMPAGINAS = 0 or 0 = 0)
      AND (D.NOMBREARCHIVO = null or null is null)
      AND (D.NEGOCIO = null or null is null)
      AND (D.NOMBREMETADATACARGA = null or null is null)
      AND (D.FECHACARGA = TO_DATE(null) or TO_DATE(null) is null);

Этот запрос возвращает result

И когда я делаю Xplain для:

enter image description here

Стоимость очень высока, но этот запрос использует индекс. Запрос длится примерно 10 секунд. Как я могу улучшить производительность запроса?

Я использую Oracle 12 c

Ответы [ 2 ]

0 голосов
/ 24 февраля 2020

Здесь задействовано несколько факторов:

Во-первых, этот запрос использует вторую (или третью) часть составного индекса, в результате чего SKIP SCAN.

посмотрите на все индексы в таблице и посмотрите, что это за индекс TIPORECIBO. Вполне вероятно, что это не ведущая колонка. Вы можете улучшить производительность, создав индекс с TIPORECIBO в качестве ведущего столбца, но это маловероятно - это столбец типа, который может иметь только несколько значений и не является хорошим кандидатом для индекса.

Вторая проблема заключается в том, что Oracle использует индекс для получения набора строк-кандидатов, а затем переходит к самим блокам данных, чтобы получить строки для дальнейшей фильтрации. select count(*) будет работать намного лучше, если Oracle не нужно извлекать блоки данных. Это может быть достигнуто путем создания индекса, который содержит все данные, необходимые для фильтра.

В вашем случае индекс для TIPORECIBO и FECHAEMISION будет означать, что Oracle может go до один индекс без необходимости доступа к блокам данных.

Третья проблема заключается в том, что вы применяете TO_DATE к столбцу FECHAEMISION. Если это тип данных DATE, то вам не нужно преобразование, и это вызывает у вас проблемы. Если вам нужно преобразование, опцией будет индекс на основе функций для TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd').

Чтобы настроить этот конкретный запрос, вы можете попробовать составной индекс на основе функций:

CREATE INDEX TB_E2V_DOCUMENTOS_CICLO_FX1 ON TB_E2V_DOCUMENTOS_CICLO(FECHAEMISION, TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd'))

Наконец, этот запрос явно генерируется из кода: такие строки, как AND (D.BA = null or null is null), по-видимому, являются способом исключения частей предложения WHERE, когда внешний интерфейс проходит NULL. Возможно, это будет AND (D.BA = 'X' or 'X' is null), если для этого параметра будет указано значение.

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

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

0 голосов
/ 18 февраля 2020

Примечания. Все предикаты "and (= null или null is null)" всегда будут иметь значение true; Oracle не определяет ноль, поэтому ноль не равен нулю, поэтому вместо этого, если вы хотите проверить на ноль, используйте «is null»

select * from dual where null = null; -- returns no rows
select * from dual where not (null <> null); -- returns no rows
select * from dual where null is null;  -- returns 1 row
select * from dual where not(null is not null);  -- returns 1 row

Что касается индексации, вам нужен индекс, который выборочный (т.е. возвращает гораздо меньше строк) и присутствует в предикате предложения where. В этом случае это выглядит как функциональный индекс TO_DATE (D.FECHAEMISION, 'yyyy / MM / dd') вместе с D.TIPORECIBO в порядке. В этом случае используется INDEX SKIP SCAN, вероятно, потому что D.TIPORECIBO не является ведущим столбцом; СКАНИРОВАНИЕ УКАЗАНИЯ ИНДЕКСА медленнее, чем СКАНИРОВАНИЕ ИНДЕКСА ДИАПАЗОНА, потому что для этого нужно прочитать больше блоков индекса.

...