Является ли замена условной логики операторами CASE эффективной в Transact-SQL?

у меня есть хранимая процедура, которая делает что-то вроде:

IF @Param = '1'
    SELECT HT.HeaderKey, HT.Description,
           (SELECT SUM(E1) -- E1 is actually a complex expression
            FROM   DetailTable DT INNER JOIN ...
                                  INNER JOIN ...
                                  INNER JOIN ...
            WHERE  DT.HeaderKey = HT.HeaderKey)
    FROM   HeaderTable HT
ELSE IF @Param = '2'
    SELECT HT.HeaderKey, HT.Description,
           (SELECT SUM(E2) -- E2 is yet another complex expression
            FROM   DetailTable DT INNER JOIN ... -- Here, DetailTable is not
                                  INNER JOIN ... -- joined to the same tables
                                  INNER JOIN ... -- as in the first case
            WHERE  DT.HeaderKey = HT.HeaderKey)
    FROM   HeaderTable HT
-- Etc. There are five cases.

Я хотели чтобы уменьшить запрос до следующего:

SELECT HT.HeaderKey, HT.Description,
       CASE @Param
         WHEN '1'
           (SELECT SUM(E1)
            FROM   DetailTable DT INNER JOIN ...
                                  INNER JOIN ...
                                  INNER JOIN ...
            WHERE  DT.HeaderKey = HT.HeaderKey)
         WHEN '2'
           (SELECT SUM(E2)
            FROM   DetailTable DT INNER JOIN ...
                                  INNER JOIN ...
                                  INNER JOIN ...
            WHERE  DT.HeaderKey = HT.HeaderKey)
         -- Etc.
         ELSE 0
       END
FROM   HeaderTable HT

однако, если SQL Server оценивает все случаи, независимо от того, какой из них будет фактически возвращен, измененный запрос будет крайне неэффективным.

таким образом, я хотел бы знать, оценивает ли SQL Server все дела в CASE оператор, или только первый, который отвечает CASEположением?

4 ответов


оператор CASE SQL Server, как указано в в этой статье, использует короткое замыкание, поэтому в показанном примере он не будет оценивать результаты каждого возможного результата случая для каждой строки.
Однако вы все равно получите менее эффективный запрос, чем ваш текущий формат, поскольку вы заставите все результаты использовать один и тот же план выполнения, который может быть не оптимальным. В зависимости от различий между подзапросами CASE эффект может быть довольно значительное.


нет, он добавляет "предикат passthru" к плану, который гарантирует, что он только оценивает необходимые запросы. См.подзапросы в выражениях CASE


предполагая, что соединения одинаковы в каждом подзапросе, я бы попробовал что-то вроде этого:

;with dt as
(
    select
        HeaderKey,
        sum(case @Param
            when 1 then E1
            when 2 then E2
            ...) as ExpressionSum
    from DetailTable DT
        inner join...
    group by dt.HeaderKey
)
select
    ht.HeaderKey,
    ht.description,
    dt.ExpressionSum
from HeaderTable HT
    inner join dt
        on HT.HeaderKey=dt.HeaderKey

или я могу быть грубым непониманием того, что вы пытаетесь сделать ;)


вы почти всегда получите более быстрый результат с решением на основе набора против процедурного решения в SQL Server (и всех RDMS).