为什么CROSS APPLY不等同于INNER JOIN

时间:2014-12-12 15:54:55

标签: sql sql-server sql-server-2012

这在2分钟内完成:

SELECT    
    G.GKey,
    Amount = SUM(fct.AmountEUR)
FROM      
    WH.dbo.vw_Fact fct 
    INNER JOIN #g G ON
       fct.DateKey >= G.Livedate AND
       fct.GKey = G.GKey 
GROUP BY G.GKey;

这将在8分钟内完成:

SELECT    
    G.GKey,
    C.Amount 
FROM      
    #g G
    CROSS APPLY
    (
        SELECT
           Amount = SUM(fct.AmountEUR)
        FROM 
           WH.dbo.vw_Fact fct 
        WHERE
           fct.DateKey >= G.Livedate AND
           fct.GKey = G.GKey 
    ) C;

这些都是非常简单的脚本,它们在逻辑上看起来与我相同。

表#G有50行,聚集索引ON #G(Livedate,GKey)WH.dbo.vw_Fact有十亿行。

我实际上觉得最初将较大的表应用于小表会更有效率。

我使用CROSS APPLY的经验是有限的 - 是否有一个明显的原因(没有探索执行计划)在缓慢的时间?

是否有可能更快的'第三种方式'?

3 个答案:

答案 0 :(得分:1)

这是两个连接之间的逻辑差异:

CROSS APPLY:在给定值LiveDateGKey上生成聚合的笛卡尔交叉积,这将针对每一行重新执行。

INNER JOIN:对于LiveDateGKey的每个值,在vw_Fact上产生一对一的匹配,然后与GKey的公共值相加,这会创建先加入set,然后应用聚合。

正如其他一些答案所提到的,当你加入一个由另一个表的某些行级数据参数化的表值函数时,交叉应用很方便。

有没有第三种方式,那就更快?我通常建议不要在连接中使用开放式运算符(例如>=)。也许尝试预先聚合GKey上的大表和一些日期桶。另外,在LiveDate上设置包含AmountEUR

的非群集密钥

答案 1 :(得分:0)

我认为你试图获得Rolling sum。使用Over() clause试试这个。

SELECT G.GKey,
       Amount = Sum(fct.AmountEUR)
                  OVER(
                    partition BY G.GKey
                    ORDER BY id rows UNBOUNDED PRECEDING)
FROM   WH.dbo.vw_Fact fct
       INNER JOIN #g G
               ON fct.GKey = G.GKey 

答案 2 :(得分:0)

APPLY逐行工作,对于更复杂的连接很有用,例如根据第一个表中的值连接表的前X行或使用参数连接函数

有关示例,请参阅here

交叉应用较慢的明显原因是它逐行工作!

因此,对于#g的每一行,您都在交叉申请中运行聚合查询。