Оптимизация запроса 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 ]

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

Часто проблемы такого типа исчезают, если вы анализируете соответствующие таблицы (поэтому у Oracle есть лучшее представление о распределении данных)

ANALYZE TABLE tasks COMPUTE STATISTICS;
3 голосов
/ 23 сентября 2008

Предложение «IN» в Oracle известно довольно медленно. Фактически, внутренний оптимизатор запросов в Oracle не может хорошо обрабатывать операторы с «IN». попробуйте использовать "EXISTS":

SELECT orderID FROM tasks WHERE orderID NOT EXISTS 
    (SELECT DISTINCT orderID FROM tasks WHERE
         engineer1 IS NOT NULL AND engineer2 IS NOT NULL)`print("code sample");`

Внимание: проверьте, строит ли запрос те же результаты данных.

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

SELECT orderID AS oid FROM tasks WHERE NOT EXISTS 
    (SELECT DISTINCT orderID AS oid2 FROM tasks WHERE
         engineer1 IS NOT NULL AND engineer2 IS NOT NULL and oid=oid2)

или что-то похожее; -)

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

Я бы попробовал использовать соединения вместо

SELECT 
    t.orderID 
FROM 
    tasks  t
    LEFT JOIN tasks t1
        ON t.orderID =  t1.orderID
        AND t1.engineer1 IS NOT NULL 
        AND t1.engineer2 IS NOT NULL
WHERE
    t1.orderID IS NULL 

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

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

(при условии, что у вас есть таблица заказов со всеми перечисленными заказами)

, который затем можно переписать, используя соединения как:

SELECT 
    o.orderID 
FROM 
    orders o
    LEFT JOIN tasks t
        ON o.orderID =  t.orderID
        AND t.engineer1 IS NOT NULL 
        AND t.engineer2 IS NOT NULL
WHERE
    t.orderID IS NULL 
2 голосов
/ 23 сентября 2008

Я согласен с TZQTZIO, я не получаю ваш запрос.

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

Название вопроса не очень полезно. Я мог бы настроить этот запрос в одной базе данных Oracle и заставить его работать медленно, а в другом - быстрее. Существует множество факторов, определяющих, как база данных разрешает запрос, статистику объектов, статистику схемы SYS и параметры, а также производительность сервера. Sqlserver против Oracle здесь не проблема.

Для тех, кто интересуется настройкой и производительностью запросов и хочет узнать больше, некоторые из терминов Google для поиска - это «дубовый стол оракул» и «оракул Джонатан Льюис».

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

Некоторые вопросы:

  • Сколько строк в заданиях?
  • Какие индексы на нем определены?
  • Была ли таблица недавно проанализирована?

Другой способ написать тот же запрос:

select orderid from tasks
minus
select orderid from tasks
where engineer1 IS NOT NULL AND engineer2 IS NOT NULL

Однако я бы предпочел, чтобы запрос включал таблицу "orders":

select orderid from ORDERS
minus
select orderid from tasks
where engineer1 IS NOT NULL AND engineer2 IS NOT NULL

или

select orderid from ORDERS
where orderid not in
( select orderid from tasks
  where engineer1 IS NOT NULL AND engineer2 IS NOT NULL
)

или

select orderid from ORDERS
where not exists
( select null from tasks
  where tasks.orderid = orders.orderid
  and   engineer1 IS NOT NULL OR engineer2 IS NOT NULL
)
1 голос
/ 23 сентября 2008

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

SELECT t1.orderID 
FROM   tasks t1
WHERE  NOT EXISTS
       (SELECT 1 
        FROM   tasks t2 
        WHERE  t2.orderID   = t1.orderID
        AND    t2.engineer1 IS NOT NULL 
        AND    t2.engineer2 IS NOT NULL)
1 голос
/ 23 сентября 2008

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

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

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

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

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

Новый дубль.

Ифф

  • Функция COUNT () не считает значения NULL

и

  • Вы хотите, чтобы orderID всех задач, где нет из этих задач, имеет Engine1 или Engineer 2 значение

затем это должно делать то, что вы хотите:

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

Пожалуйста, проверьте это.

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

Оптимизатор Oracle отлично справляется с обработкой операторов MINUS. Если вы перепишете свой запрос с помощью MINUS, он, вероятно, будет выполнен довольно быстро:

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

Какая пропорция строк в таблице соответствует условию "engineer1 NOT NULL, а engineer2 NOT NULL"?

Это говорит вам (примерно), стоит ли пытаться использовать индекс для извлечения связанных ордеридов.

Другой способ написать запрос в Oracle, который бы очень хорошо обрабатывал неиндексированные случаи, был бы:

select distinct orderid
from
(
select orderid,
       max(case when engineer1 is null and engineer2 is null then 0 else 1)
          over (partition by orderid)
          as max_null_finder
from   tasks
)
where max_null_finder = 0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...