【问题标题】:Fast calculation of partial sums on a large SQL Server table在大型 SQL Server 表上快速计算部分总和
【发布时间】:2013-09-16 20:52:00
【问题描述】:

我需要在当前有超过 400k 行并准备进一步增长的表上计算截至指定日期的列总数。我发现 SUM() 聚合函数对于我的目的来说太慢了,因为对于超过 50k 行的总和,我无法获得超过 1500 毫秒的速度。

请注意,下面的代码是迄今为止我发现的最快的实现。值得注意的是,从CustRapport 过滤数据并将其存储在临时表中,使我的性能提高了 3 倍。我也尝试过索引,但它们通常会使它变慢。

然而,我希望该函数至少快一个数量级。关于如何实现这一目标的任何想法?我偶然发现了http://en.wikipedia.org/wiki/Fenwick_tree。但是,我宁愿在 SQL Server 中处理存储和计算。

CustRapportCustLeistung 是具有以下定义的视图:

ALTER VIEW [dbo].[CustLeistung] AS 
SELECT TblLeistung.* FROM TblLeistung 
WHERE WebKundeID IN (SELECT WebID FROM XBauAdmin.dbo.CustKunde)

ALTER VIEW [dbo].[CustRapport] AS 
SELECT MainRapport.* FROM MainRapport 
WHERE WebKundeID IN (SELECT WebID FROM XBauAdmin.dbo.CustKunde)

感谢您的帮助或建议!

ALTER FUNCTION [dbo].[getBaustellenstunden] 
(
    @baustelleID int,
    @datum date
)
RETURNS 
@ret TABLE 
(
    Summe float
)
AS
BEGIN

    declare @rapport table
    (
        id int null
    )

    INSERT INTO @rapport select WebSourceID from CustRapport 
    WHERE RapportBaustelleID = @baustelleID AND RapportDatum <= @datum

    INSERT INTO @ret
    SELECT      SUM(LeistungArbeit)
    FROM CustLeistung INNER JOIN @rapport as r ON LeistungRapportID = r.id 
    WHERE LeistungArbeit is not null 
         AND LeistungInventarID is null AND LeistungArbeit > 0

    RETURN 
END

执行计划:

http://s23.postimg.org/mxq9ktudn/execplan1.png

http://s23.postimg.org/doo3aplhn/execplan2.png

【问题讨论】:

  • 请添加您在表 CustLeistung 和 CustRapport 上的索引。另外,您在表变量中插入了多少条记录?
  • RapportDatum 的数据类型是什么?是date吗?即没有时间分量?
  • RapportDatumdatetime
  • 请提供函数Portal_common_MyKundeUser 的定义,因为它似乎在此处弄乱了优化器。 CusteKunde 似乎也是一个视图,所以我们也需要这个定义。
  • 对于今天仍然来这里的人:this post 从 2012 年指向 SUM() + OVER() + ROWS 作为一种非常快速的方法

标签: sql performance sql-server-2008 sum aggregate-functions


【解决方案1】:

在您提供更多信息之前,我现在可以提供一般性建议。

更新了我的查询,因为它是从视图中直接从表中提取的。

INSERT INTO @ret
    SELECT
        SUM(LeistungArbeit)
    FROM (
        SELECT DISTINCT WebID FROM XBauAdmin.dbo.CustKunde
    ) Web
    INNER JOIN dbo.TblLeistung ON TblLeistung.WebKundeID=web.webID
    INNER JOIN dbo.MainRapport ON MainRapport.WebKundeID=web.webID 
        AND TblLeistung.LeistungRapportID=MainRapport.WebSourceID
        AND MainRapport.RapportBaustelleID = @baustelleID
        AND MainRapport.RapportDatum <= @datum
   WHERE TblLeistung.LeistungArbeit is not null 
   AND TblLeistung.LeistungInventarID is null
   AND TblLeistung.LeistungArbeit > 0
  1. 摆脱表变量。它们有它们的用途,但是当我获得超过 100 条记录时,我会切换到临时表;根据我的经验,索引临时表的性能更好。
  2. 将您的选择更新为上述查询并重新测试性能
  3. 检查并确保查询中的每个列引用都有索引。如果您使用显示实际执行计划,SQL Server 将帮助确定索引在哪里有用。

【讨论】:

  • 关于第一点 #temp 表不能在函数中使用(尽管我看到你建议的重写无论如何都不会使用它们。
  • 感谢您的建议。这只是我最初解决方案的一半左右。这个解决方案的问题似乎是昂贵的 RapportDatum
  • @RobinBruegger 请尝试我更新的查询。另外,请在表格上发布索引定义
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
  • 2010-10-26
  • 1970-01-01
相关资源
最近更新 更多