Оценка CTE в SQL Server 2005 - PullRequest
       24

Оценка CTE в SQL Server 2005

4 голосов
/ 26 марта 2010

У меня есть вопрос о том, как MS SQL оценивает функции внутри CTE. Несколько поисков не дали никаких результатов, связанных с этой проблемой, но я прошу прощения, если это общеизвестно, и я просто за кривой. Это был бы не первый раз: -)

Этот запрос является упрощенной (и, очевидно, менее динамичной) версией того, что я на самом деле делаю, но он показывает проблему, с которой я сталкиваюсь. Это выглядит так:

CREATE TABLE #EmployeePool(EmployeeID int, EmployeeRank int);

INSERT INTO #EmployeePool(EmployeeID, EmployeeRank)
  SELECT 42, 1
  UNION ALL 
  SELECT 43, 2;

DECLARE @NumEmployees int;
SELECT @NumEmployees  = COUNT(*) FROM #EmployeePool;

WITH RandomizedCustomers AS (
  SELECT CAST(c.Criteria AS int) AS CustomerID,
         dbo.fnUtil_Random(@NumEmployees) AS RandomRank
    FROM dbo.fnUtil_ParseCriteria(@CustomerIDs, 'int') c)
SELECT rc.CustomerID,
       ep.EmployeeID
  FROM RandomizedCustomers rc
  JOIN #EmployeePool ep ON ep.EmployeeRank = rc.RandomRank;

DROP TABLE #EmployeePool;

Для всех приведенных выше действий можно предположить следующее:

  • Результатом dbo.fnUtil_Random() всегда является значение int, большее нуля и меньшее или равное переданному аргументу. Так как он вызывается выше с @NumEmployees, который имеет значение 2, эта функция всегда вычисляет до 1 или 2.

  • В результате dbo.fnUtil_ParseCriteria(@CustomerIDs, 'int') создается таблица из одной строки в одну строку, которая содержит sql_variant с базовым типом 'int' со значением 219935.

Учитывая вышеизложенные предположения, имеет смысл (во всяком случае, для меня), что результат вышеприведенного выражения всегда должен создавать таблицу из двух столбцов, содержащую одну запись - CustomerID и EmployeeID. CustomerID должен всегда иметь значение int 219935, а EmployeeID должно быть 42 или 43.

Однако это не всегда так. Иногда я получаю ожидаемую единственную запись. В других случаях я получаю две записи (по одной для каждого EmployeeID), а в другие я не получаю никаких записей. Однако, если я заменю RandomizedCustomers CTE на истинную временную таблицу, проблема полностью исчезнет.

Каждый раз, когда я думаю, что у меня есть объяснение этому поведению, оказывается, что оно не имеет смысла или невозможно, поэтому я буквально не могу объяснить, почему это произойдет. Так как проблема не возникает, когда я заменяю CTE временной таблицей, я могу только предположить, что она как-то связана с функциями внутри CTE, которые оцениваются при соединениях с этим CTE. У кого-нибудь из вас есть теории?

1 Ответ

3 голосов
/ 26 марта 2010
Оптимизатор

SQL Server может решать, следует ли переоценивать CTE или нет.

Например, этот запрос:

WITH    q AS
        (
        SELECT  NEWID() AS n
        )
SELECT  *
FROM    q
UNION ALL
SELECT  *
FROM    q

создаст два разных NEWID(), однако, если вы используете кэшированный план XML для преобразования CTE в операцию Eager Spool, записи будут одинаковыми.

...