SQL Group By每个日期的总和并使用最大日期

时间:2016-05-09 11:08:04

标签: mysql sql group-by max

我已经看过很多类似的问题,但没有什么能够解决我的特殊问题。

我有一个表格,为每个帐户存储多个位置。更改存储为增量。因此,例如在第1天以下......

AC_ID | POS_ID | ASAT       | VAL
    1 |      1 | 2016-01-01 | 100
    1 |      2 | 2016-01-01 | 200

2016年01月1日AC_ID 1的总值为300.第二天它可能会更新为...

AC_ID | POS_ID | ASAT       | VAL
    1 |      1 | 2016-01-01 | 100
    1 |      2 | 2016-01-01 | 200
    1 |      2 | 2016-01-02 | 250

现在AC_ID 1的总值为350.这是因为POS_ID 2的新记录会覆盖之前的记录,但POS_ID 1的值未更改。为了删除POS_ID 1,表格将更改为......

AC_ID | POS_ID | ASAT       | VAL
    1 |      1 | 2016-01-01 | 100
    1 |      2 | 2016-01-01 | 200
    1 |      2 | 2016-01-02 | 250
    1 |      1 | 2016-01-03 | 0

现在价值在第3天变为250。

我可以使用像这样的子查询计算任何给定日期的值

SELECT SUM(VAL) FROM POSITION P1
WHERE P1.ASAT = 
  (SELECT MAX(P2.ASAT) FROM POSITION P2
   WHERE P1.AC_ID  = P2.AC_ID
   AND   P1.POS_ID = P2.POS_ID
   AND   P2.DATE <= [CHOSEN DATE])

我现在要做的是编写一个查询,它会为每个AC_ID的每个ASAT提供总价值。如果不是delta存储机制,我可以使用

轻松实现这一点
SELECT AC_ID, ASAT, SUM(VAL) FROM POSITION
GROUP BY AC_ID, ASAT
ORDER BY ASAT DESC

我正在寻找的东西将实现上述目标,但考虑到桌面上的联接。如果我使用上述内容,那么我只会获得ASAT日期发生变化的所有内容的总计,而不是所有未更改的现有值。

在上面的示例中,应该等同于

的结果集
AC_ID | ASAT       | SUM(VAL)
    1 | 2016-01-01 |      300
    1 | 2016-01-02 |      350
    1 | 2016-01-03 |      250

这是数据与输出的另一个例子

AC_ID | POS_ID | ASAT       | VAL
    1 |      1 | 2016-01-01 | 100
    1 |      2 | 2016-01-01 | 200
    1 |      2 | 2016-01-02 | 250
    1 |      1 | 2016-01-03 | 0
    2 |      1 | 2016-01-02 | 500
    3 |      7 | 2016-01-02 | 1000
    3 |      7 | 2016-01-03 | 1000
    3 |     12 | 2016-01-03 | 5000
    2 |      1 | 2016-01-04 | 750

结果

AC_ID | ASAT       | SUM(VAL)
    1 | 2016-01-01 |      300
    1 | 2016-01-02 |      350
    1 | 2016-01-03 |      250
    2 | 2016-01-02 |      500
    2 | 2016-01-04 |      750
    3 | 2016-01-02 |     1000
    3 | 2016-01-03 |     6000

我改变了它的工作方式

虽然下面的答案奏效但是它们的表现非常糟糕(不是作者的错!)为了使这个得到可接受的东西(我需要亚秒返回)我重构了表格以包含{{1列。此列在每个插入上更新以设置该行的生命周期。如果某行没有替代条目,则结束日期设置为9999-12-31。我上面的例子变成......

end_date

然后我可以从接受的答案中删除第二个连接,并在内连接中添加一个额外的子句。

AC_ID | POS_ID | ASAT       | END_DATE   | VAL
    1 |      1 | 2016-01-01 | 2016-01-03 |  100
    1 |      2 | 2016-01-01 | 2016-01-02 |  200
    1 |      2 | 2016-01-02 | 9999-12-31 |  250
    1 |      1 | 2016-01-03 | 9999-12-31 |    0
    2 |      1 | 2016-01-02 | 2016-01-04 |  500
    3 |      7 | 2016-01-02 | 2016-01-03 | 1000
    3 |      7 | 2016-01-03 | 9999-12-31 | 1000
    3 |     12 | 2016-01-03 | 9999-12-31 | 5000
    2 |      1 | 2016-01-04 | 9999-12-31 |  750

2 个答案:

答案 0 :(得分:2)

这应该可以满足您的需求:

SELECT
    P1.ac_id,
    P1.asat,
    SUM(P2.val) AS total_value
FROM
    (SELECT DISTINCT P.ac_id, P.asat FROM dbo.Position P) P1
INNER JOIN dbo.Position P2 ON
    P2.ac_id = P1.ac_id AND
    P2.asat <= P1.asat
LEFT OUTER JOIN dbo.Position P3 ON
    P3.ac_id = P1.ac_id AND
    P3.pos_id = P2.pos_id AND
    P3.asat > P2.asat AND
    P3.asat <= P1.asat
WHERE
    P3.ac_id IS NULL
GROUP BY
    P1.ac_id,
    P1.asat

该查询会获取您的所有ac_id / asat组合,然后抓取可能属于需要合计的行的所有行,最后使用LEFT OUTER JOIN并检查NULL消除任何不是特定pos_id的最新行。

答案 1 :(得分:1)

这不是特别有效,但我认为它应该做你想要的:

SELECT aa.AC_ID, aa.ASAT,  SUM(p.VAL)
FROM (SELECT DISTINCT AC_ID, ASAT FROM POSITION
     ) aa JOIN
     POSITION P
     ON p.AC_ID = aa.AC_ID and p.ASAT <= aa.ASAT
WHERE P.ASAT = (SELECT MAX(P2.ASAT)
                FROM POSITION P2
                WHERE P.AC_ID  = P2.AC_ID AND
                      P.POS_ID = P2.POS_ID AND
                      P2.ASAT <= aa.ASAT
               )
GROUP BY aa.AC_ID, aa.ASAT;