Оптимизация запросов - PullRequest
       6

Оптимизация запросов

1 голос
/ 22 декабря 2009

У меня есть запрос, который я хочу выполнить как можно быстрее.

Вот оно:

select d.InvoiceDetailId,a.Fee,a.FeeTax
from InvoiceDetail d
LEFT JOIN InvoiceDetail a on a.AdjustDetailId = d.InvoiceDetailId

Я поместил восходящий индекс в столбец AdjustDetailId

Затем я выполнил запрос с «Показать фактический план выполнения», и оценочная стоимость поддерева для результата (вне самого верхнего узла выбора) составила 2,07

Я тогда подумал, может быть, я смогу что-то сделать, чтобы улучшить это, поэтому я добавил условное выражение в левое соединение следующим образом:

select d.InvoiceDetailId,a.Fee,a.FeeTax
from InvoiceDetail d
LEFT JOIN InvoiceDetail a on a.AdjustDetailId is not null 
and a.AdjustDetailId = d.InvoiceDetailId

Я перезапустился и получил стоимость поддерева 0,98. Так что я подумал, здорово, я сделал это в два раза быстрее. Затем я щелкнул «Показать статистику клиента», а затем нажал «Выполнить» 4-5 раз с обоими запросами и поверил, что первый запрос был усреднен быстрее. Я не понимаю Кстати, запрос возвращает 120 тыс. Строк.

Есть идеи?

Может быть, я получаю испорченные результаты из-за кэширования, но я не знаю, так ли это или как сбросить кэширование.

EDIT: Хорошо, я гуглил, как очистить кеш запросов, поэтому перед запросами добавил следующее:

DBCC DROPCLEANBUFFERS  
DBCC FREEPROCCACHE

Затем я выполнил каждый запрос 5 раз, и первый запрос был еще немного быстрее (13%). 1-й запрос: клиент Время обработки: 239,4 2-й запрос: клиент Время обработки: 290

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

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

РЕДАКТИРОВАТЬ # 2: Мне нужно получить все InvoiceDetails, а не только скорректированные, следовательно, левое соединение.

РЕДАКТИРОВАТЬ # 3: Реальная проблема, которую я пытаюсь решить с помощью запроса, заключается в суммировании всех строк InvoiceDetail, но в то же время и их корректировке. В конечном счете, кажется, что лучший запрос для выполнения - следующий. Я думал, что объединение, а затем добавление объединенной таблицы будет единственным способом, но кажется, что группировка по условию решает проблему наиболее элегантно.

SELECT CASE WHEN AdjustDetailId IS NULL THEN InvoiceDetailId ELSE AdjustDetailId END AS InvoiceDetailId
  ,SUM(Fee + FeeTax) AS Fee
FROM dbo.InvoiceDetail d
GROUP BY CASE WHEN AdjustDetailId IS NULL THEN InvoiceDetailId ELSE AdjustDetailId END

Пример: со следующими строками InvoiceDetailID | Стоимость | FeeTax | AdjustDetailId

1 | 300 | 0 | NULL

2 | -100 | 0 | 1

3 | -50 | 0 | 1

4 | 250 | 0 | NULL

Мое желание было получить следующее: InvoiceDetailID | Плата 1 | 150

4 | 250

Спасибо всем за ваш вклад.

Ответы [ 4 ]

5 голосов
/ 22 декабря 2009

Если вы хотите сделать этот запрос действительно быстрым, вам нужно

  • превратить левое соединение в внутреннее соединение
  • убедитесь, что InvoiceDetail.AdjustDetailId и InvoiceDetail.InvoiceDetailId проиндексированы

    SELECT 
      d.InvoiceDetailId, a.Fee, a.FeeTax
    FROM 
      dbo.InvoiceDetail d
    INNER JOIN 
      dbo.InvoiceDetail a ON a.AdjustDetailId = d.InvoiceDetailId
    

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

Чтобы обновить статистику, используйте команду UPDATE STATISTICS (table) - см. Документы MSDN по ОБНОВЛЕНИЮ СТАТИСТИКИ здесь

2 голосов
/ 22 декабря 2009

Я бы предположил, что они будут одинаковыми (с тем же планом выполнения), поскольку для предиката типа a.AdjustDetailId = d.InvoiceDetailId невозможно быть истинным, если одна сторона равна нулю ... Условие Not Null является избыточным. Но, возможно, процессор выполняет дополнительные ненужные шаги с этим дополнительным предикатом ...

Но то, что упоминает другой ответ, более важно. Вам действительно нужно вывести все строки, где нет соответствующей записи (Счета без корректирующего счета) ?? Если нет, измените его на Внутреннее соединение, и оно сильно ускорится.

если они вам действительно нужны, вы можете попробовать Union

  Select d.InvoiceDetailId,a.Fee,a.FeeTax
  From InvoiceDetail d
     Join InvoiceDetail a 
         On a.AdjustDetailId = d.InvoiceDetailId
  Union
  Select InvoiceDetailId, null, null
  from InvoiceDetail 
  Where AdjustDetailId Is Null

Что делает то же самое без использования внешнего соединения ... (Проблематично, будут ли два запроса с объединением выполняться быстрее, чем один запрос внешнего соединения ...)

1 голос
/ 23 декабря 2009

Для ваших запросов я могу придумать 3 различных разумных плана выполнения:

LOOP JOIN OUTER [a.AdjustDetailId = d.InvoiceDetailId]
    TABLE SCAN InvoiceDetail d
    TABLE SCAN InvoiceDetail a

HASH JOIN OUTER [a.AdjustDetailId = d.InvoiceDetailId]
    TABLE SCAN InvoiceDetail d
    TABLE SCAN InvoiceDetail a

LOOP JOIN OUTER
    HASH JOIN OUTER [x.AdjustDetailId = d.InvoiceDetailId] AS y
        TABLE SCAN InvoiceDetail d
        INDEX SEEK [InvoiceDetail, AdjustDetailId IS NOT NULL] x
    InvoiceDetail a [a.AdjustDetailId = y.AdjustDetailId]

Возможно, добавление условия IS NOT NULL заставит оптимизатор выбрать другой план, трудно сказать.

1 голос
/ 22 декабря 2009

У вас есть только 1 таблица в этом запросе, верно?

Если вы используете

выберите InvoiceDetailId, Fee, FeeTax от InvoiceDetail

Это БУДЕТ получить все строки, а не только скорректированные.

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

Вы также можете попробовать "включить" столбцы Fee и FeeTax в свой индекс - это очень поможет, если таблица действительно широкая.

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