Влияние упорядочения коррелированных подзапросов в проекции - PullRequest
3 голосов
/ 31 марта 2010

Я заметил нечто неожиданное в том, как SQL Server (в данном случае SQL Server 2008) обрабатывает коррелированные подзапросы в операторе select. Мое предположение заключалось в том, что на план запроса не должен влиять простой порядок, в котором подзапросы (или столбцы, в этом отношении) записываются в предложении projection оператора select. Однако, похоже, это не так.

Рассмотрим следующие два запроса, которые идентичны, за исключением порядка подзапросов в CTE:

--query 1: subquery for Color is second
WITH vw AS
(
 SELECT p.[ID],
  (SELECT TOP(1) [FirstName] FROM [Preference] WHERE p.ID = ID AND [FirstName] IS NOT NULL ORDER BY [LastModified] DESC) [FirstName],
  (SELECT TOP(1) [Color] FROM [Preference] WHERE p.ID = ID AND [Color] IS NOT NULL ORDER BY [LastModified] DESC) [Color]
 FROM Person p
)
SELECT ID, Color, FirstName
FROM vw
WHERE Color = 'Gray';


--query 2: subquery for Color is first
WITH vw AS
(
 SELECT p.[ID],
  (SELECT TOP(1) [Color] FROM [Preference] WHERE p.ID = ID AND [Color] IS NOT NULL ORDER BY [LastModified] DESC) [Color],
  (SELECT TOP(1) [FirstName] FROM [Preference] WHERE p.ID = ID AND [FirstName] IS NOT NULL ORDER BY [LastModified] DESC) [FirstName]
 FROM Person p
)
SELECT ID, Color, FirstName
FROM vw
WHERE Color = 'Gray';

Если вы посмотрите на два плана запросов, то увидите, что внешнее объединение используется для каждого подзапроса и что порядок соединений совпадает с порядком написания подзапросов. К результату внешнего объединения применяется фильтр для цвета, чтобы отфильтровать строки, в которых цвет не «серый». (Мне странно, что SQL использовал бы внешнее соединение для цветового подзапроса, так как у меня есть ненулевое ограничение на результат цветового подзапроса, но все в порядке.)

Большинство строк удаляются цветным фильтром. В результате запрос 2 значительно дешевле запроса 1, поскольку при втором соединении задействовано меньше строк. Все причины для создания такого утверждения в стороне, это ожидаемое поведение? Разве SQL-сервер не должен перемещать фильтр как можно раньше в плане запроса, независимо от порядка написания подзапросов?

Редактировать: Просто чтобы уточнить, есть веская причина, по которой я изучаю этот сценарий. Возможно, мне понадобится создать представление, которое включает аналогично сконструированные подзапросы, и теперь очевидно, что любая фильтрация на основе этих столбцов, спроецированных из представления, будет отличаться по производительности только из-за упорядочения столбцов!

Ответы [ 2 ]

2 голосов
/ 31 марта 2010

С появлением оператора TOP здесь Оптимизатор запросов удивительно слеп по статистике, поэтому он будет искать другие подсказки о том, как лучше всего с ним работать, например, сначала создавать соответствующие части CTE.

И это внешнее соединение, потому что подзапрос будет использоваться как NULL, если ничего не будет возвращено, и система сначала создаст его экземпляр. Если бы вы использовали агрегат вместо TOP, вы, вероятно, получили бы немного другой, но более последовательный план.

1 голос
/ 31 марта 2010

Вот альтернативная версия, которая может работать лучше:

With Colors As
    (
    Select Id, [Color]
        , ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY [LastModified] DESC ) As Num
    From Preference
    Where [Color] Is Not Null
    )
    , Names As
    (
    Select Id, [FirstName]
        , ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY [LastModified] DESC ) As Num
    From Preference
    Where [FirstName] Is Not Null
    )
Select
From Person As P
    Join Colors As C
        On C.Id = P.Id
            And C.Num = 1
    Left Join Names As N
        On N.Id = P.Id
            And N.Num = 1
Where C.[Color]= 'Grey'

Другое решение, которое является более кратким, но может или не работать так же хорошо:

With RankedItems
    (
    Select Id, [Color], [FirstName]
        , ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY Case When [Color] Is Not Null 1 Else 0 End DESC, [LastModified] DESC ) As ColorRank
        , ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY Case When [FirstName] Is Not Null 1 Else 0 End DESC, [LastModified] DESC ) As NameRank
    From Preference
    )
Select
From Person As P
    Join RankedItems As RI
        On RI.Id = P.Id
            And RI.ColorRank = 1
    Left Join RankedItems As RI2
        On RI2.Id = P.Id
            And RI2.NameRank = 1
Where RI.[Color]= 'Grey'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...