【问题标题】:T-sql problem with running sum运行 sum 的 T-sql 问题
【发布时间】:2009-10-18 20:12:57
【问题描述】:

我正在尝试编写 T-sql 脚本,该脚本将为一个表找到“打开”记录

数据结构如下

Id (int PK)      Ts (datetime)       Art_id (int)     Amount (float)
1                '2009-01-01'        1                1
2                '2009-01-05'        1                -1
3                '2009-01-10'        1                1
4                '2009-01-11'        1                -1
5                '2009-01-13'        1                1
6                '2009-01-14'        1                1
7                '2009-01-15'        2                1
8                '2009-01-17'        2                -1
9                '2009-01-18'        2                1

根据我的需要,我试图仅显示每篇文章的最后总和之后的记录,其中 0 按最后一次运行总和的零值的日期排序。因此,我试图为 Art_id=1 抽象(显示)记录 5 和 6,为 art_id=2 抽象(显示)记录 9。我正在使用 MSSQL2005,我的表有大约 30K 条记录,其中有 6000 个不同的 ART_ID 值。

【问题讨论】:

  • 为什么 Art_id=1 只记录 5 和 6?为什么不也记录 3?
  • @RBarryYoung art_id 值 1 的总和为 0,日期为 '2009-01-11',对于记录 3,我们可以暗示以记录 4 结束

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


【解决方案1】:

在这个解决方案中,我只想找到所有行 没有该 Art_id 的后续行,其中运行总和为 0。我假设我们可以使用 ID 作为更好与 TS 相比,决胜局,因为两行可以具有相同的时间戳,但它们将获得顺序标识值。

;WITH base AS
(
    SELECT
        ID, Art_id, TS, Amount,
        RunningSum = Amount + COALESCE
        (
            (
               SELECT SUM(Amount)
                FROM dbo.foo
                WHERE Art_id = f.Art_id 
                AND ID < f.ID
            )
            , 0
        )
    FROM dbo.[table name] AS f
)
SELECT ID, Art_id, TS, Amount
FROM base AS b1
WHERE NOT EXISTS
(
    SELECT 1 
    FROM base AS b2 
    WHERE Art_id = b1.Art_id
    AND ID >= b1.ID
    AND RunningSum = 0
)
ORDER BY ID;

【讨论】:

    【解决方案2】:

    完整的工作查询:

    SELECT 
      * 
    FROM TABLE_NAME E
    JOIN
      (SELECT
        C.ART_ID,
        MAX(TS) MAX_TS
      FROM
        (SELECT 
          ART_ID,
          TS,
          COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
        FROM TABLE_NAME A) C
      WHERE C.ROW_SUM = 0
      GROUP BY C.ART_ID) D
    ON 
      (D.ART_ID = E.ART_ID) AND
      (E.TS >= D.MAX_TS)
    

    首先我们计算每一行的运行总和:

    SELECT 
      ART_ID,
      TS,
      COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
    FROM TABLE_NAME A
    

    然后我们找最后一篇带 0 的文章:

    SELECT
      C.ART_ID,
      MAX(TS) MAX_TS
    FROM
      (SELECT 
        ART_ID,
        TS,
        COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
      FROM TABLE_NAME A) C
    WHERE C.ROW_SUM = 0
    GROUP BY C.ART_ID
    

    【讨论】:

    • 这行得通!与我的回答不同:您不会显示只有 1 行的文章;我假设 id 随着时间的推移而增加;您假设没有两行具有完全相同的 TS 日期。
    • '你不显示只有 1 行的文章' - 我不明白。 '你假设没有两行有完全相同的 TS 日期' - 是的,因为你没有写在这种情况下机制应该如何表现。
    • 如果您假设 ID 随时间增加,您可以将 (B.Ts
    • 感谢您的宝贵时间和帮助,现在我的时区已经过了午夜,暂时我无法测试查询,您让明天是星期天,妻子又要发疯了,因为我在周末工作。关于同一日期,我也有以毫秒为单位的实时记录时间戳,我会尝试实施,这可能是比寻找更高 ID 更好的解决方案,再次感谢您的时间
    • @LukLeg:假设文章 4 只有 1 行 (10,'2009-01-18',4,1),该行不会出现在您的查询中
    【解决方案3】:

    您可以使用以下命令找到运行总和为零的所有行:

    select cur.id, cur.art_id
    from @articles cur
    left join @articles prev
        on prev.art_id = cur.art_id
        and prev.id <= cur.id
    group by cur.id, cur.art_id
    having sum(prev.amount) = 0
    

    然后您可以查询所有行之后的所有行,运行总和为零:

    select a.*
    from @articles a
    left join (
        select cur.id, cur.art_id, running = sum(prev.amount)
        from @articles cur
        left join @articles prev
            on prev.art_id = cur.art_id
            and prev.ts <= cur.ts
        group by cur.id, cur.art_id
        having sum(prev.amount) = 0
    ) later_zero_running on
        a.art_id = later_zero_running.art_id
        and a.id <= later_zero_running.id
    where later_zero_running.id is null
    

    LEFT JOIN 结合 WHERE 表示:在这一行之后不能有一行,其运行总和为零。

    【讨论】:

      猜你喜欢
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-26
      • 2011-11-23
      相关资源
      最近更新 更多