【问题标题】:Most Efficient Way to Compute Running Value in SQL [duplicate]在 SQL 中计算运行值的最有效方法 [重复]
【发布时间】:2011-12-15 19:24:32
【问题描述】:

可能重复:
Calculate a Running Total in SqlServer

考虑这些数据

     Day | OrderCount
       1       3
       2       2 
       3       11
       4       3
       5       6

如何使用 T-SQL 查询获得 OrderCount(running value) 结果集的累积

      Day | OrderCount | OrderCountRunningValue
       1        3            3
       2        2            5
       3        11           16
       4        3            19
       5        6            25

我可以通过在实际查询中循环(使用#table)或在我的 C# 代码隐藏中轻松地做到这一点,但是当我处理数千条记录时它太慢了(考虑到我每天也收到订单)所以我正在寻找为了更好/更有效的方法,希望没有循环,比如递归 CTE 或其他东西。

任何想法将不胜感激。 TIA

【问题讨论】:

标签: sql common-table-expression


【解决方案1】:
SELECT t.Day, 
       t.OrderCount, 
       (SELECT SUM(t1.OrderCount) FROM table t1 WHERE t1.Day <= t.Day) 
         AS OrderCountRunningValue
FROM table t

【讨论】:

  • 请注意,这在较大数据集上的扩展性很差。第 100 条记录将计算 100 行。第 101 行将重新计算所有这些行,然后再计算一个。等等等等。它相当于一个半笛卡尔积。
【解决方案2】:
SELECT 
  t.day, 
  t.orderCount, 
  SUM(t1.orderCount) orderCountRunningValue
FROM 
  table t INNER JOIN table t1 ON t1.day <= t.day
group by t.day,t.orderCount

【讨论】:

  • 与 KayKay 的答案相同的考虑因素(尽管它使用不同的表达式)它遵循半笛卡尔积的相同逻辑。
  • 啊,我明白了,同一张桌子,但别名不同
【解决方案3】:

由于您似乎需要在客户端而不是在另一个 SQL 查询中使用这些结果,因此最好不要在 SQL 中执行此操作。

(如果确实有必要,我评论中的链接问题显示了 SQL 中的“最佳”选项。)


建议将 Day 和 OrderCount 值作为一个结果集 (SELECT day, orderCount FROM yourTable ORDER BY day),然后在 C# 中计算运行总计。

您的 C# 代码将能够有效地遍历数据集,并且几乎肯定会胜过 SQL 方法。这样做的目的是将一些负载从 SQL Server 转移到 Web 服务器,但总体上(并且显着)节省了资源。

【讨论】:

    【解决方案4】:

    CTE 的救援(再次):

    DROP TABLE tmp.sums;
    CREATE TABLE tmp.sums
            ( id INTEGER NOT NULL
            , zdate timestamp not null
            , amount integer NOT NULL
            );
    
    INSERT INTO tmp.sums (id,zdate,amount) VALUES
     (1, '2011-10-24', 1 ),(1, '2011-10-25', 2 ),(1, '2011-10-26', 3 )
    ,(2, '2011-10-24', 11 ),(2, '2011-10-25', 12 ),(2, '2011-10-26', 13 )
            ;
    
    WITH RECURSIVE list AS (
    -- Terminal part
        SELECT  t0.id, t0.zdate
        , t0.amount AS amount
        , t0.amount AS runsum
        FROM tmp.sums t0
        WHERE NOT EXISTS (
            SELECT * FROM tmp.sums px
            WHERE px.id = t0.id
            AND px.zdate < t0.zdate
            )
        UNION
        -- Recursive part
        SELECT  p1.id AS id
        , p1.zdate AS zdate
        , p1.amount AS amount
        , p0.runsum + p1.amount AS runsum
        FROM tmp.sums AS p1
        , list AS p0
        WHERE p1.id = p0.id
        AND p0.zdate < p1.zdate
        AND NOT EXISTS (
            SELECT * FROM tmp.sums px
            WHERE px.id = p1.id
            AND px.zdate < p1.zdate
            AND px.zdate > p0.zdate
            )
        )
    SELECT * FROM list
    ORDER BY id, zdate;
    

    输出:

    DROP TABLE
    CREATE TABLE
    INSERT 0 6
     id |        zdate        | amount | runsum 
    ----+---------------------+--------+--------
      1 | 2011-10-24 00:00:00 |      1 |      1
      1 | 2011-10-25 00:00:00 |      2 |      3
      1 | 2011-10-26 00:00:00 |      3 |      6
      2 | 2011-10-24 00:00:00 |     11 |     11
      2 | 2011-10-25 00:00:00 |     12 |     23
      2 | 2011-10-26 00:00:00 |     13 |     36
    (6 rows)
    

    【讨论】:

    • 你的意思是像问题的cmets中提到的问题?哪里有人建议这是一个重复的问题,看看这些问题会提供这样的答案吗?
    • 不,我不查看重复项。这是一个 100% 的原始答案。我怀疑是否有人提出了递归解决方案。
    • 现在我看到有一个。马丁史密斯。对我来说没有坏公司。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 2017-10-18
    • 2011-03-22
    • 1970-01-01
    • 2017-07-17
    • 2012-06-11
    • 2011-03-11
    相关资源
    最近更新 更多