【问题标题】:T-SQL rolling twelve month per day performanceT-SQL 每天滚动 12 个月的性能
【发布时间】:2012-05-28 21:46:18
【问题描述】:

我检查过类似的问题,但没有一个对我有用。最有用的是http://forums.asp.net/t/1170815.aspx/1,但性能让我的查询运行了好几个小时。

我有 150 万条记录,基于 4 年的产品销售(大约 1 万件产品)。我想要一个包含日期、产品和滚动十二个月销售额的表格。

这个查询(来自上面的链接)有效,并显示了我想要的,但性能使它毫无用处:

select day_key, product_key, price, (select sum(price) as R12 from #ORDER_TURNOVER as tb1 where tb1.day_key <= a.day_key and tb1.day_key > dateadd(mm, -12, a.day_key) and tb1.product_key = a.product_key) as RSum into #hejsan
from #ORDER_TURNOVER as a

我为所有记录尝试了滚动求和游标功能,速度快如闪电,但我无法获得仅对过去 365 天的销售额求和的查询。

非常感谢有关如何解决此问题的任何想法。 谢谢。

【问题讨论】:

  • 您使用的是什么版本的 SQL Server?从外观上看,您可以从使用 SQL Server 2012 sum() over(order by ...) 中受益。
  • 我们使用 2005 年并将继续使用它至少一年以上。不过感谢您的提示!
  • 您正在使用临时表。请告诉我,您在该临时表上的 (product_key, day_key) 上有一个索引?
  • 你不能把一天的总和,存储起来,收集最近的365总和吗?或者:生成年总和,每天减去今天的日总和-365,再加上今天的日总和?将其和每日总和存储起来,以便您只需要重新计算今天的总和。
  • Dems:我当然有索引。使用索引每个产品大约需要 30 秒,而不需要大约 2 分钟。

标签: sql-server performance tsql sql-server-2005


【解决方案1】:

我会稍微改变你的设置。

首先,有一个表格,列出所有感兴趣的产品密钥...

CREATE TABLE product (
  product_key    INT NOT NULL,
  price          INT,
  some_fact_data VARCHAR(MAX),
  what_ever_else SOMEDATATYPE,
  PRIMARY KEY CLUSTERED (product_key)
)

然后,我将有一个日历表,其中包含您可能需要报告的每个日期...

CREATE TABLE calendar (
  date             SMALLDATETIME,
  is_bank_holdiday INT,
  what_ever_else   SOMEDATATYPE,
  PRIMARY KEY CLUSTERED (date)
)

最后,我会确保您的数据表在所有相关字段上都有一个覆盖索引...

CREATE INDEX IX_product_day ON #ORDER_TURNOVER (product_key, day_key)

这将允许以下查询...

SELECT
  product.product_key,
  product.price,
  calendar.date,
  SUM(price)    AS RSum
FROM
  product
CROSS JOIN
  calendar
INNER JOIN
  #ORDER_TURNOVER AS data
    ON  data.product_key = product.product_key
    AND data.day_key    >  dateadd(mm, -12, calendar.date)
    AND data.day_key    <= calendare.date
GROUP BY
  product.product_key,
  product.price,
  calendar.date

通过以这种方式进行所有操作,每个 product/calendar_date 组合将与数据表中的一组记录相关,这些记录彼此连续。对于优化器来说,这将使查找要聚合的数据的行为变得更加简单。

[需要一个索引,特别是在顺序(产品、日期)中。]

如果你有相反的索引,它实际上要困难得多......

示例数据:

 product | date                   date | product
---------+-------------    ------------+---------
    A    |  01/01/2012      01/01/2012 |    A
    A    |  02/01/2012      01/01/2012 |    B
    A    |  03/01/2012      02/01/2012 |    A
    B    |  01/01/2012      02/01/2012 |    B
    B    |  02/01/2012      03/01/2012 |    A
    B    |  03/01/2012      03/01/2012 |    B

在左侧 oyu 中,只需获取 365 天块中彼此相邻的所有记录。

您可以在右侧搜索每条记录,然后才能进行汇总。搜索相对简单,但你做了 365 个。比左边的版本多很多。

【讨论】:

    【解决方案2】:

    这就是在 SQL Server 2005-2008 中“运行总计”/“汇总子集”的方式。在 SQL 2012 中,原生支持运行总计,但我们仍在使用 2005-2008 db

    SELECT  day_key ,
            product_key ,
            price ,
            ( SELECT    SUM(price) AS R12
              FROM      #ORDER_TURNOVER AS tb1
              WHERE     tb1.day_key <= a.day_key
                        AND tb1.day_key > DATEADD(mm, -12, a.day_key)
                        AND tb1.product_key = a.product_key
            ) AS RSum
    INTO    #hejsan
    FROM    #ORDER_TURNOVER AS a 
    

    一些建议。

    您可以预先计算运行总计,这样就不会一次又一次地计算它们。你在做什么上面的选择是一个变相的循环而不是一个集合查询(除非优化器可以将子查询转换为连接)。

    上述解决方案需要对代码进行一些更改。

    您当然可以尝试的另一个解决方案是在您的#ORDER_TURNOVER 临时表上创建一个聚集索引。这是更安全的,因为它是本地更改。

    CREATE CLUSTERED INDEX IndexName
    ON #ORDER_TURNOVER (day_key,day_key,product_key)
    

    您在 WHERE 子句中的所有 3 个表达式都是 SARGS,因此优化器现在将执行搜索而不是扫描。

    如果索引解决方案没有提供足够的性能提升,那么值得投资解决方案 1

    【讨论】:

    • 我已经尝试过索引解决方案,但它仍然太慢。我将如何预先计算运行总数?谢谢。
    猜你喜欢
    • 1970-01-01
    • 2022-08-02
    • 1970-01-01
    • 2013-10-06
    • 2019-01-24
    • 1970-01-01
    • 1970-01-01
    • 2021-02-11
    • 1970-01-01
    相关资源
    最近更新 更多