首页 > 解决方案 > SQL Server CASE 表达式不会短路吗?

问题描述

如果有人可以帮助我们优化以下查询,则合适

根据执行平面,无论条件如何,else 部分子查询似乎总是在执行。

不会CASE短路吗?为什么它正在执行,即使它不是必需的?

 IF OBJECT_ID('#Calculation') IS NOT NULL
    DROP TABLE #Calculation;

SELECT 
    Result.IdDeckungsbeitrag,
    Result.wert AS Wert,
    REPLACE(@Formula, '<#PackagingCosts> ', Result.wert) AS Kalkulation
INTO
    #Calculation
FROM
    (SELECT 
         deck.IdDeckungsbeitrag,
         CASE
            WHEN lp.ID_VERPACKUNG_2 IS NULL
                    AND lp.ID_VERPACKUNG_3 IS NULL 
               THEN vg.VERPACKUNGSKOSTEN_PRO_EINHEIT * deck.Menge
               ELSE
           (
               SELECT SUM(temp.me) * vg.VERPACKUNGSKOSTEN_PRO_EINHEIT
               FROM
               (
                   SELECT SUM(gv.MENGE) AS me
                   FROM dbo.KUNDENRECHNUNG_POSITION krp
                       LEFT JOIN dbo.LIEFERSCHEIN_POSITION lp
                           ON lp.ID_LIEFERSCHEIN_POSITION = krp.ID_LIEFERSCHEIN_POSITION
                       LEFT JOIN dbo.GEBINDE_VERLADEN gv
                           ON gv.ID_LIEFERSCHEIN_POSITION = lp.ID_LIEFERSCHEIN_POSITION
                       LEFT JOIN dbo.MATERIAL_BESTAND mb
                           ON mb.ID_MATERIAL_BESTAND = gv.ID_MATERIAL_BESTAND
                       LEFT JOIN dbo.MATERIAL_GEBINDE mg
                           ON mg.ID_MATERIAL_BESTAND = mg.ID_MATERIAL_BESTAND
                   WHERE mg.CHARGE_NUMMER = deck.Charge
                   GROUP BY mg.ID_VERPACKUNG
               ) temp
           )
           END AS wert
    FROM @DeckungsbeitragCalculationPositions_TVP deck
        LEFT JOIN dbo.KUNDENRECHNUNG_POSITION krp
            ON krp.ID_KUNDENRECHNUNG_POSITION = deck.IdDeckungsbeitrag
        LEFT JOIN dbo.LIEFERSCHEIN_POSITION lp
            ON lp.ID_LIEFERSCHEIN_POSITION = krp.ID_LIEFERSCHEIN_POSITION
        LEFT JOIN dbo.VERPACKUNG vg
            ON vg.ID_VERPACKUNG = lp.ID_VERPACKUNG_1
    WHERE deck.IdMandant = @Id_Mandant
) Result;

标签: sql-servercasequery-optimization

解决方案


不要认为它是否短路。这是一种程序代码思维方式,而不是基于集合的思维方式。

在 SQL 中,实际的“执行”发生在表或索引扫描和查找、哈希匹配、排序等领域。将不同表中的数据拉入工作集中,最终将产生所需的关系结果。

没有看到任何实际的或预计的执行计划,在这种情况下(没有双关语),我怀疑查询优化器认为首先在内存中生成这个结果集是最有效的:

SELECT mg.ID_VERPACKUNG, mg.CHARGE_NUMMER, SUM(gv.MENGE) AS me
FROM dbo.KUNDENRECHNUNG_POSITION krp
LEFT JOIN dbo.LIEFERSCHEIN_POSITION lp
    ON lp.ID_LIEFERSCHEIN_POSITION = krp.ID_LIEFERSCHEIN_POSITION
LEFT JOIN dbo.GEBINDE_VERLADEN gv
    ON gv.ID_LIEFERSCHEIN_POSITION = lp.ID_LIEFERSCHEIN_POSITION
LEFT JOIN dbo.MATERIAL_BESTAND mb
    ON mb.ID_MATERIAL_BESTAND = gv.ID_MATERIAL_BESTAND
LEFT JOIN dbo.MATERIAL_GEBINDE mg
    ON mg.ID_MATERIAL_BESTAND = mg.ID_MATERIAL_BESTAND
GROUP BY mg.ID_VERPACKUNG, mg.CHARGE_NUMMER

这是子句的子查询ELSE,减去WHERE子句条件,并添加了附加信息SELECT以使匹配更有效。如果查询优化器不能确信在WHEN高比例的时间内满足子句,它可能认为生成这个更大的ELSE集合一次以根据需要进行匹配更有效。换句话说,如果它认为无论如何都必须运行该子查询,它可能会尝试为所有可能的数据预加载它。

我们对您的数据库知之甚少,无法提出真正的解决方案,但围绕 、 和 字段建立索引ID_VERPACKUNG_2可能ID_VERPACKUNG_3CHARGE_NUMMER有所帮助。您还可以使用 CTE、临时表或表变量来帮助 Sql Server 更好地缓存这些数据一次。


推荐阅读