【问题标题】:CROSS APPLY too slow for running total - TSQLCROSS APPLY 太慢,无法运行总计 - SQL
【发布时间】:2013-09-01 23:09:19
【问题描述】:

请查看下面的代码,因为使用 CROSS APPLY 运行速度太慢。

如何删除 CROSS APPLY 并添加其他运行速度更快的内容? 请注意我使用的是 SQL Server 2008 R2。

;WITH MyCTE AS
(
   SELECT 
      R.NetWinCURRENCYValue AS NetWin
     ,dD.[Date]             AS TheDay
   FROM   
      dimPlayer AS P
   JOIN 
      dbo.factRevenue AS R ON P.playerKey = R.playerKey
   JOIN 
      dbo.vw_Date AS dD ON Dd.dateKey = R.dateKey
   WHERE    
      P.CustomerID   = 12345)
SELECT 
     A.TheDay               AS [Date]
    ,ISNULL(A.NetWin, 0)    AS NetWin
    ,rt.runningTotal        AS CumulativeNetWin
FROM MyCTE AS A
CROSS APPLY (SELECT SUM(NetWin) AS runningTotal 
                  FROM MyCTE WHERE TheDay <= A.TheDay) AS rt
ORDER BY A.TheDay

【问题讨论】:

标签: performance tsql sql-server-2008-r2 cross-apply


【解决方案1】:
CREATE TABLE #temp (NetWin money, TheDay datetime)
insert into #temp 
SELECT 
      R.NetWinCURRENCYValue AS NetWin
     ,dD.[Date]             AS TheDay
   FROM   
      dimPlayer AS P
   JOIN 
      dbo.factRevenue AS R ON P.playerKey = R.playerKey
   JOIN 
      dbo.vw_Date AS dD ON Dd.dateKey = R.dateKey
   WHERE    
      P.CustomerID   = 12345;

SELECT 
     A.TheDay               AS [Date]
    ,ISNULL(A.NetWin, 0)    AS NetWin
    ,SUM(B.NetWin)          AS CumulativeNetWin
FROM #temp AS A 
JOIN #temp AS B 
  ON A.TheDay >= B.TheDay
GROUP BY A.TheDay, ISNULL(A.NetWin, 0);

【讨论】:

  • 如果给出正确答案,则实现 CTE。创建一个临时表而不是 CTE。 CTE 只是语法,因此它实际上是被评估的。如果您需要样品,请告诉我。
  • 不知道谁投了你的票,但我投了赞成票并批准了答案,谢谢你的帮助!
  • CTE 很可能很昂贵并且被多次评估。
【解决方案2】:

这里https://stackoverflow.com/a/13744550/613130建议使用递归CTE。

;WITH MyCTE AS
(
    SELECT 

           R.NetWinCURRENCYValue AS NetWin
          ,dD.[Date]             AS TheDay
          ,ROW_NUMBER() OVER (ORDER BY dD.[Date]) AS RN

    FROM   dimPlayer             AS P

        JOIN dbo.factRevenue     AS R   ON P.playerKey = R.playerKey
        JOIN dbo.vw_Date         AS dD  ON Dd.dateKey = R.dateKey

    WHERE    P.CustomerID   = 12345

)

, MyCTERec AS
(
    SELECT  C.TheDay               AS [Date]
           ,ISNULL(C.NetWin, 0)    AS NetWin
           ,ISNULL(C.NetWin, 0)    AS CumulativeNetWin
           ,C.RN

    FROM MyCTE AS C
    WHERE C.RN = 1

    UNION ALL

    SELECT  C.TheDay               AS [Date]
           ,ISNULL(C.NetWin, 0)    AS NetWin
           ,P.CumulativeNetWin + ISNULL(C.NetWin, 0)    AS CumulativeNetWin
           ,C.RN

    FROM MyCTERec P
    INNER JOIN MyCTE AS C ON C.RN = P.RN + 1
)

SELECT * 
    FROM MyCTERec 
    ORDER BY RN
    OPTION (MAXRECURSION 0)

请注意,如果您有 1 条记录 == 1 天,则此查询将起作用!如果一天有多条记录,结果会与其他查询不同。

【讨论】:

  • 有的情况下一天会有超过1条记录,那该如何处理呢?
  • @Etienne 那么你不能使用这个解决方案(否则每条记录会有不同的总数......但也许没关系......在 10.00 累积是 10 美元,在 11.00 累积是 20 美元...)
【解决方案3】:

正如我所说的here,如果你想要真正快速的计算,把它放到带有顺序主键的临时表中,然后计算滚动总数:

create table #Temp (
    ID bigint identity(1, 1) primary key,
    [Date] date,
    NetWin decimal(29, 10)
)

insert into #Temp ([Date], NetWin)
select
    dD.[Date],
    sum(R.NetWinCURRENCYValue) as NetWin,
from dbo.dimPlayer as P
    inner join dbo.factRevenue as R on P.playerKey = R.playerKey
    inner join dbo.vw_Date as dD on Dd.dateKey = R.dateKey
where P.CustomerID = 12345
group by dD.[Date]
order by dD.[Date]

;with cte as (
    select T.ID, T.[Date], T.NetWin, T.NetWin as CumulativeNetWin
    from #Temp as T
    where T.ID = 1

    union all

    select T.ID, T.[Date], T.NetWin, T.NetWin + C.CumulativeNetWin as CumulativeNetWin
from cte as C
    inner join #Temp as T on T.ID = C.ID + 1
)
select C.[Date], C.NetWin, C.CumulativeNetWin
from cte as C
order by C.[Date]

我假设您可以在输入中有重复的日期,但不希望在输出中出现重复,因此我在将数据放入表格之前对其进行了分组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-07
    • 2014-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多