Оптимизация запроса SELECT, который выполняется медленно в Oracle, который быстро выполняется в SQL Server - PullRequest
12 голосов
/ 23 сентября 2008

Я пытаюсь запустить следующий оператор SQL в Oracle, и для его запуска требуется много лет:

SELECT orderID FROM tasks WHERE orderID NOT IN 
(SELECT DISTINCT orderID FROM tasks WHERE
 engineer1 IS NOT NULL AND engineer2 IS NOT NULL)

Если я запускаю только часть, которая находится в предложении IN, она очень быстро выполняется в Oracle, т.е.

SELECT DISTINCT orderID FROM tasks WHERE
engineer1 IS NOT NULL AND engineer2 IS NOT NULL

Почему все утверждение занимает так много времени в Oracle? В SQL Server весь оператор выполняется быстро.

В качестве альтернативы, я должен использовать более простой / другой / лучший оператор SQL?

Еще несколько подробностей о проблеме:

  • Каждый заказ состоит из множества задач
  • Каждый ордер будет распределен (одна или несколько задач будут иметь установлен engineer1 и engineer2) или ордер может быть нераспределенным (все его задачи имеют нулевые значения для полей инженера)
  • Я пытаюсь найти все нераспределенные идентификаторы заказов.

На всякий случай, если есть какая-то разница, в таблице ~ 120 тыс. Строк и 3 задания на каждый заказ, поэтому ~ 40 тыс. Различных заказов.

Ответы на ответы:

  • Я бы предпочел оператор SQL, который работает как в SQL Server, так и в Oracle.
  • У заданий есть только индекс для orderID и taskID.
  • Я попробовал версию оператора NOT EXISTS, но она работала более 3 минут, прежде чем я ее отменил. Возможно, нужна версия заявления JOIN?
  • Существует также таблица "orders" со столбцом orderID. Но я пытался упростить этот вопрос, не включив его в исходный оператор SQL.

Я полагаю, что в исходном операторе SQL подзапрос выполняется каждый раз для каждой строки в первой части оператора SQL - даже если он статический и его нужно запускать только один раз?

Выполнение

ANALYZE TABLE tasks COMPUTE STATISTICS;

заставил мою первоначальную инструкцию SQL выполняться намного быстрее.

Хотя мне все еще интересно, почему я должен это делать, и если / когда мне нужно будет запустить его снова?

Статистика дает Oracle информация оптимизатора на основе затрат, которая нужно определить эффективность различных планов выполнения: для Например, количество строк в таблице, средняя ширина рядов, максимальная и самые низкие значения в столбце, количество отдельные значения на столбец, кластеризация коэффициент показателей и т. д.

В небольшой базе данных вы можете просто настроить работа по сбору статистики каждую ночь и оставь это в покое. На самом деле это по умолчанию под 10g. Для большего реализации вы обычно должны взвесить стабильность исполнения планы против того, как данные изменения, что является хитрым балансом.

Oracle также имеет функцию под названием «динамическая выборка», которая используется для образцы таблиц для определения соответствующих статистика во время исполнения. Это гораздо чаще используется с данными склады, где накладные расходы выборка перевесила потенциальное увеличение производительности для длительный запрос.

Ответы [ 18 ]

0 голосов
/ 23 сентября 2008

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

Что касается ответов, чем проще, тем лучше в этом случае. Забудьте подзапросы, объединения, отдельные и групповые байты, они вообще не нужны!

0 голосов
/ 23 сентября 2008

Разве ваш запрос не совпадает с

SELECT orderID FROM tasks
WHERE engineer1 IS NOT NULL OR engineer2 IS NOT NULL

0 голосов
/ 23 сентября 2008

Вот альтернативный подход, который, я думаю, дает то, что вы хотите:

SELECT orderID
 FROM tasks
 GROUP BY orderID
 HAVING COUNT(engineer1) = 0 OR COUNT(engineer2) = 0

Я не уверен, хотите ли вы, чтобы "И" или "ИЛИ" в предложении HAVING. Похоже, согласно бизнес-логике эти два поля должны быть либо заполнены, либо оба равны NULL; если это гарантировано, то вы можете свести условие к простой проверке engineer1.

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

0 голосов
/ 23 сентября 2008

Подзапросы «плохие» в Oracle. Обычно лучше использовать соединения.

Вот статья о том, как переписать ваши подзапросы с помощью join: http://www.dba -oracle.com / SQL / t_rewrite_subqueries_performance.htm

0 голосов
/ 23 сентября 2008

Другой вариант - использовать МИНУС (КРОМЕ MSSQL)

SELECT orderID FROM tasks
MINUS
SELECT DISTINCT orderID FROM tasks WHERE engineer1 IS NOT NULL 
AND engineer2 IS NOT NULL
0 голосов
/ 23 сентября 2008

Как насчет:

SELECT DISTINCT orderID FROM tasks t1 WHERE NOT EXISTS (SELECT * FROM tasks t2 WHERE t2.orderID=t1.orderID AND (engineer1 IS NOT NULL OR engineer2 IS NOT NULL));

Я не гуру оптимизации, но, возможно, вы также пропустили некоторые индексы в своей базе данных Oracle.

0 голосов
/ 12 октября 2008

Я согласен с ΤΖΩΤΖΙΟΥ и wearejimbo, что ваш запрос должен быть ...

SELECT DISTINCT orderID FROM Tasks 
WHERE Engineer1 IS NULL OR Engineer2 IS NULL;

Я не знаю, что такое SQL Server, но этот запрос не сможет использовать какие-либо индексы, потому что пустые строки отсутствуют в индексах. Решением этой проблемы было бы переписать запрос таким образом, чтобы можно было создать индекс на основе функции, включающий только строки с нулевыми значениями. Это может быть сделано с NVL2, но, вероятно, не будет переносимым на SQL Server.

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

0 голосов
/ 23 сентября 2008

Если у вас нет индекса по столбцам Engineer1 и Engineer2, то вы всегда будете генерировать сканирование таблицы в SQL Server и эквивалентный ему код в Oracle.

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

SELECT DISTINCT orderID 
FROM tasks 
WHERE (engineer1 IS NULL OR engineer2 IS NULL)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...