2013-03-15 4 views
5

Почему этот запрос быстрее в SQL Server 2008 R2 (версия 10.50.2806.0)запросов быстрее с верхним атрибутом

SELECT 
     MAX(AtDate1), 
     MIN(AtDate2) 
    FROM 
    (
     SELECT TOP 1000000000000 
      at.Date1 AS AtDate1, 
      at.Date2 AS AtDate2 
     FROM 
      dbo.tab1 a 
     INNER JOIN 
      dbo.tab2 at 
     ON 
      a.id = at.RootId 
     AND CAST(GETDATE() AS DATE) BETWEEN at.Date1 AND at.Date2 
     WHERE 
      a.Number = 223889 
    )B 

затем

SELECT 
     MAX(AtDate1), 
     MIN(AtDate2) 
    FROM 
    (
     SELECT 
      at.Date1 AS AtDate1, 
      at.Date2 AS AtDate2 
     FROM 
      dbo.tab1 a 
     INNER JOIN 
      dbo.tab2 at 
     ON 
      a.id = at.RootId 
     AND CAST(GETDATE() AS DATE) BETWEEN at.Date1 AND at.Date2 
     WHERE 
      a.Number = 223889 
    )B 

?

Второе утверждение с атрибутом TOP в шесть раз быстрее.

Внутренний подзапрос count(*) - это 9280 строк.

Могу ли я использовать СОВЕТ, чтобы объявить, что оптимизатор SQL Server делает это правильно?

+0

Чтобы убедиться, вам нужно взглянуть на план выполнения. Вероятно, он использует более эффективный путь с TOP, чем без него. – Khan

+2

Это может быть вызвано проблемой оптимизатора. Можете ли вы предоставить планы выполнения для обоих запросов? –

+3

Лучший способ предоставления планов выполнения - запустить их в SSMS с включенным опцией «Query -> Include Actual Execution Plan», а затем загрузить версию XML до сайта, такого как pastebin. См. [Как предоставить план выполнения кому-либо для анализа?] (Http://meta.dba.stackexchange.com/questions/796/how-do-i-provide-an-execution-plan-to-someone- для анализа) для большего. –

ответ

3

Я вижу, что вы отправили the plans. Просто удача вничью.

Ваш фактический запрос состоит из 16 табличных соединений.

SELECT max(atDate1) AS AtDate1, 
     min(atDate2) AS AtDate2, 
     max(vtDate1) AS vtDate1, 
     min(vtDate2) AS vtDate2, 
     max(bgtDate1) AS bgtDate1, 
     min(bgtDate2) AS bgtDate2, 
     max(lftDate1) AS lftDate1, 
     min(lftDate2) AS lftDate2, 
     max(lgtDate1) AS lgtDate1, 
     min(lgtDate2) AS lgtDate2, 
     max(bltDate1) AS bltDate1, 
     min(bltDate2) AS bltDate2 
FROM (SELECT TOP 100000 at.Date1 AS atDate1, 
          at.Date2 AS atDate2, 
          vt.Date1 AS vtDate1, 
          vt.Date2 AS vtDate2, 
          bgt.Date1 AS bgtDate1, 
          bgt.Date2 AS bgtDate2, 
          lft.Date1 AS lftDate1, 
          lft.Date2 AS lftDate2, 
          lgt.Date1 AS lgtDate1, 
          lgt.Date2 AS lgtDate2, 
          blt.Date1 AS bltDate1, 
          blt.Date2 AS bltDate2 
     FROM dbo.Tab1 a 
       INNER JOIN dbo.Tab2 at 
       ON a.id = at.Tab1Id 
        AND cast(Getdate() AS DATE) BETWEEN at.Date1 AND at.Date2 
       INNER JOIN dbo.Tab5 v 
       ON v.Tab1Id = a.Id 
       INNER JOIN dbo.Tab16 g 
       ON g.Tab5Id = v.Id 
       INNER JOIN dbo.Tab3 vt 
       ON v.id = vt.Tab5Id 
        AND cast(Getdate() AS DATE) BETWEEN vt.Date1 AND vt.Date2 
       LEFT OUTER JOIN dbo.Tab4 vk 
       ON v.id = vk.Tab5Id 
       LEFT OUTER JOIN dbo.VerkaufsTab3 vkt 
       ON vk.id = vkt.Tab4Id 
       LEFT OUTER JOIN dbo.Plu p 
       ON p.Tab4Id = vk.Id 
       LEFT OUTER JOIN dbo.Tab15 bg 
       ON bg.Tab5Id = v.Id 
       LEFT OUTER JOIN dbo.Tab7 bgt 
       ON bgt.Tab15Id = bg.Id 
        AND cast(Getdate() AS DATE) BETWEEN bgt.Date1 AND bgt.Date2 
       LEFT OUTER JOIN dbo.Tab11 b 
       ON b.Tab15Id = bg.Id 
       LEFT OUTER JOIN dbo.Tab14 lf 
       ON lf.Id = b.Id 
       LEFT OUTER JOIN dbo.Tab8 lft 
       ON lft.Tab14Id = lf.Id 
        AND cast(Getdate() AS DATE) BETWEEN lft.Date1 AND lft.Date2 
       LEFT OUTER JOIN dbo.Tab13 lg 
       ON lg.Id = b.Id 
       LEFT OUTER JOIN dbo.Tab9 lgt 
       ON lgt.Tab13Id = lg.Id 
        AND cast(Getdate() AS DATE) BETWEEN lgt.Date1 AND lgt.Date2 
       LEFT OUTER JOIN dbo.Tab10 bl 
       ON bl.Tab11Id = b.Id 
       LEFT OUTER JOIN dbo.Tab6 blt 
       ON blt.Tab10Id = bl.Id 
        AND cast(Getdate() AS DATE) BETWEEN blt.Date1 AND blt.Date2 
     WHERE a.Nummer = 223889) B 

На обоих хорошие и плохие планы План выполнения показывает «Причина досрочного прекращения Statement оптимизации», как «Time Out».

В двух планах несколько разных заказов на соединение.

Единственное соединение в планах, которые не удовлетворены поиском индекса, это то, что на Tab9. Это 63 926 строк.

Отсутствующие данные индекса в плане выполнения предлагают создать следующий индекс.

CREATE NONCLUSTERED INDEX [miising_index] 
ON [dbo].[Tab9] ([Date1],[Date2]) 
INCLUDE ([Tab13Id]) 

Проблемная часть плохого плана может быть четко видна в SQL Sentry Plan Explorer, оценки

Bad Plan

SQL Server, который 1.349174 строки будет возвращена из предыдущего присоединяется вступления в объединении на Tab9. И, следовательно, затраты на вложенные петли соединяются так, как если бы ему нужно было выполнить сканирование на внутренней таблице 1.349174 раза.

Фактически в это соединение входит 2600 строк, что означает, что оно составляет 2600 полных сканирований Tab9 (2,600 * 63,926 = 164,569,600 строк.)

Как раз так получается, что по хорошему плану предполагаемое количество строк, входящих в соединение, равно 2.74319. Это все еще неверно на три порядка, но немного увеличенная оценка означает, что SQL Server предпочитает хеш-соединение. Хэш присоединиться только делает один проход через Tab9

Good Plan

Я бы первый попробовать добавить отсутствующий индекс на Tab9.

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

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

Только в качестве последнего средства я бы рассмотрел использование подсказок для запроса, чтобы попытаться заставить план с хеш-соединением. Ваши варианты для этого - либо подсказка USE PLAN, в этом случае вы определяете именно тот план, который вы хотите, включая все типы и заказы на объединение, или указав LEFT OUTER HASH JOIN tab9 .... Этот второй вариант также имеет побочный эффект от фиксации всех заказов на соединение в плане. Оба означают, что SQL Server будет сильно ограничен, так это его способность корректировать план с изменениями в распределении данных.

1

Трудно ответить, не зная размера и структуры ваших таблиц, а также не видеть весь план выполнения. Но разница в обоих планах заключается в объединении Hash Match для запроса «top n» vs Nested Loop для другого. Hash Match - это очень ресурсоемкое соединение, потому что сервер должен подготовить хэш-ведра, чтобы использовать его. Но он становится намного более эффективным для больших таблиц, в то время как Nested Loops, сравнивая каждую строку в одной таблице с каждой строкой в ​​другой таблице, отлично подходит для небольших таблиц, потому что такой подготовки не требуется. Я думаю, что, выбрав TOP 1000000000000 строк в подзапросе, вы даете оптимизатору подсказку о том, что вы подзапрос будет производить большое количество данных, поэтому он использует Hash Match. Но на самом деле выход небольшой, поэтому Nested Loops работает лучше. То, что я только что сказал, основано на клоках информации, поэтому, пожалуйста, сердечно критикуйте мой ответ;).

+0

это план выполнения запросов http://www.dateiupload.net/download.php?file = 0875b65fb14d01c90b48c5fd146a36e3 – JJANSSEN

+0

спасибо за ответ. как я могу дать оптимизатору подсказку использовать хеш-соединение, поэтому я могу удалить верхний атрибут – JJANSSEN