2013-11-19 2 views
0

У меня возникла проблема со следующим запросом, возвращающим результаты слишком медленно, и я подозреваю, что у меня нет чего-то основного. Моя первоначальная догадка заключается в том, что оператор CASE слишком долго обрабатывает свой результат по базовым данным. Но это могло быть что-то в производных таблицах.Слишком длинный запрос - Оптимизация

Вопрос в том, как я могу ускорить это? Есть ли вопиющие ошибки в том, как я беру данные? Я где-то сталкиваюсь с проблемами сортировки или циклов? Запрос длится около 40 секунд, что кажется довольно длинным. C# - мой основной опыт, SQL - это работа.

Примечание. Я не прошу «написать свой код» или «исправить свой код». Просто для указателя в правильном направлении, я не могу понять, где происходит замедление. Каждая производная таблица выполняется очень быстро (менее секунды) сама по себе, соединения кажутся правильными, и набор результатов возвращает именно то, что мне нужно. Это слишком медленно, и я уверен, что там есть лучший SQL-скриптер;) Любые советы будут очень признательны!

SELECT 
hdr.taker 
, hdr.order_no 
, hdr.po_no as display_po 
, cust.customer_name 
, hdr.customer_id 
, 'INCORRECT-LARGE ORDER' + CASE 
        WHEN (ext_price_calc >= 600.01 and ext_price_calc <= 800) and fee_price.unit_price <> round(ext_price_calc * -.01,2) 
         THEN '-1%: $' + cast(cast(ext_price_calc * -.01 as decimal(18,2)) as varchar(255)) 
        WHEN ext_price_calc >= 800.01 and ext_price_calc <= 1000 and fee_price.unit_price <> round(ext_price_calc * -.02,2) 
         THEN '-2%: $' + cast(cast(ext_price_calc * -.02 as decimal(18,2)) as varchar(255)) 
        WHEN ext_price_calc > 1000 and fee_price.unit_price <> round(ext_price_calc * -.03,2) 
         THEN '-3%: $' + cast(cast(ext_price_calc * -.03 as decimal(18,2)) as varchar(255)) 
        ELSE 
         'OK' 
        END AS Status 
FROM 
(myDb_view_oe_hdr hdr 
LEFT OUTER JOIN myDb_view_customer cust 
ON hdr.customer_id = cust.customer_id) 
LEFT OUTER JOIN wpd_view_sales_territory_by_customer territory 
ON cust.customer_id = territory.customer_id 
LEFT OUTER JOIN 
    (select 
     order_no, 
     SUM(ext_price_calc) as ext_price_calc 
    from 
    (select 
     hdr.order_no, 
     line.item_id, 
     (line.qty_ordered - isnull(qty_canceled,0)) * unit_price as ext_price_calc 
    from myDb_view_oe_hdr hdr 
    left outer join myDb_view_oe_line line 
    on hdr.order_no = line.order_no 
    where 
     line.delete_flag = 'N' 
     AND line.cancel_flag = 'N' 
     AND hdr.projected_order = 'N' 
     AND hdr.delete_flag = 'N' 
     AND hdr.cancel_flag = 'N' 
     AND line.item_id not in ('LARGE-ORDER-1%','LARGE-ORDER-2%', 'LARGE-ORDER-3%', 'FUEL','NET-FUEL', 'CONVENIENCE-FEE')) as line 
    group by order_no) as order_total 
    on hdr.order_no = order_total.order_no 
LEFT OUTER JOIN 
    (select 
     order_no, 
     count(order_no) as convenience_count 
    from oe_line with (nolock) 
    left outer join inv_mast inv with (nolock) 
    on oe_line.inv_mast_uid = inv.inv_mast_uid 
    where inv.item_id in ('LARGE-ORDER-1%','LARGE-ORDER-2%', 'LARGE-ORDER-3%') 
     and oe_line.delete_flag <> 'Y' 
    group by order_no) as fee_count 
on hdr.order_no = fee_count.order_no 
INNER JOIN 
    (select 
     order_no, 
     unit_price 
    from oe_line line with (nolock) 
    where line.inv_mast_uid in (select inv_mast_uid from inv_mast with (nolock) where item_id in ('LARGE-ORDER-1%','LARGE-ORDER-2%', 'LARGE-ORDER-3%'))) as fee_price 
ON fee_count.order_no = fee_price.order_no 
WHERE 
    hdr.projected_order = 'N' 
    AND hdr.cancel_flag = 'N' 
    AND hdr.delete_flag = 'N' 
    AND hdr.completed = 'N' 
    AND territory.territory_id = ‘CUSTOMERTERRITORY’ 
    AND ext_price_calc > 600.00 
    AND hdr.carrier_id <> '100004' 
    AND fee_count.convenience_count is not null 
    AND CASE 
      WHEN (ext_price_calc >= 600.01 and ext_price_calc <= 800) and fee_price.unit_price <> round(ext_price_calc * -.01,2) 
       THEN '-1%: $' + cast(cast(ext_price_calc * -.01 as decimal(18,2)) as varchar(255)) 
      WHEN ext_price_calc >= 800.01 and ext_price_calc <= 1000 and fee_price.unit_price <> round(ext_price_calc * -.02,2) 
       THEN '-2%: $' + cast(cast(ext_price_calc * -.02 as decimal(18,2)) as varchar(255)) 
      WHEN ext_price_calc > 1000 and fee_price.unit_price <> round(ext_price_calc * -.03,2) 
       THEN '-3%: $' + cast(cast(ext_price_calc * -.03 as decimal(18,2)) as varchar(255)) 
      ELSE 
       'OK' END <> 'OK' 
+1

«Мое первоначальное предположение» --- программирование - это не то, о чем вы догадываетесь. Возьмите план выполнения и убедитесь в этом. – zerkms

+2

Также: CodeReview (http://codereview.stackexchange.com/) был создан для этого вопроса – AndyG

+0

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

ответ

2

Подобно тому, как ключ к правильному направлению для оптимизации:

  • Когда вы делаете OUTER JOIN для запроса с вычисляемыми столбцами, вы гарантируете не только полное сканирование таблицы, но эти расчеты должны выполняться против каждой строки в объединенной таблице. Похоже, что вы действительно можете подключиться к oe_line без вычисления столбцов (то есть путем фильтрации ext_price_calc для определенного диапазона).

  • Вам не нужно выполнять большинство подзапросов, которые находятся в вашем запросе - основной запрос может быть переработан для использования стандартного синтаксиса соединения таблицы. Присоединения к подзапросам, содержащим подзапросы, представляет проблему для оптимизатора SQL, которую он может не удовлетворять. Но, используя регулярные объединения, оптимизатор имеет гораздо больше шансов определить более эффективные стратегии запросов.

  • Вы не указали, какой SQL-механизм вы используете. Каждая база данных имеет собственные расширения, которые могут обеспечить более быстрые или более эффективные запросы. Было бы проще предоставить полезную обратную связь, если бы вы указали, используете ли вы MySQL, SQL Server, Oracle и т. Д.

  • Независимо от базы данных, которую вы используете, просмотр плана запроса всегда является хорошим местом для начала. Это скажет вам, где большая часть ввода-вывода и времени в вашем запросе расходуется.

  • Просто по общему принципу убедитесь, что ваши статистические данные являются актуальными.

0

Я сильно подозреваю, что проблема заключается в количестве соединений, которые вы делаете. Многие базы данных объединяются в основном путем системной проверки всех возможных комбинаций различных таблиц как действительных - поэтому, если вы присоединяетесь к таблицам A и B в столбце C, а A выглядит так: Имя: C Fred: 1 Алиса : 2 Бетти: 3

В то время как B выглядит следующим образом: C: Pet 1: Аллигатор 2: Lion 3: T-Rex

Когда вы джойн, он проверяет все 9 возможностей: Фред: 1: 1: Аллигатор Фред: 1: 2: Лев Фред: 1: 3: Т-рекс Алиса: 2: 1: Аллигатор Алиса: 2: 2: лев Алиса: 2: 3: Т-Рекс Бетти: 3: 1: Аллигатор Бетти: 3: 2: лев Бетти: 3: 3: Т -Rex

и проходит через и удаляет несоответствующий из них: Фред: 1: 1: Alligator Alice: 2: 2: Lion Бетти: 3: 3: T-Rex

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

... и вы делаете сколько соединений и подзапросов?

+0

«Проблема заключается в количестве объединений, которые вы делаете» - это определенно неправильно. 10 объединений не могут быть проблемой сами по себе. – zerkms

+0

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

1

Это не может быть разрешено ни одним из нас без реальных материалов для тестирования.

ЕСЛИ это так, и никто больше не отвечает на этот вопрос, я все равно могу помочь. Вот как это сделать.

(1) Выбирайте соединения и куски один за другим.
(2) это вызовет ошибки. Удалите или подделайте ссылки, чтобы избавиться от них.
(3) Посмотрите, как это работает.
(4) Положите предметы назад, прежде чем пытаться взять что-то еще.
(5) отслеживать ...
(6) также следует знать, где удаление чего-то может резко уменьшить набор результатов.

Возможно, вам не хватает указателя или какого-либо другого дымящегося пистолета.

Смежные вопросы