SQL Group By 每个日期的总和并使用最大日期
Posted
技术标签:
【中文标题】SQL Group By 每个日期的总和并使用最大日期【英文标题】:SQL Group By with Sum for each date and using max date 【发布时间】:2016-05-09 11:08:03 【问题描述】:我见过很多类似的问题,但没有什么能完全解决我的具体问题。
我有一个表格,为每个帐户存储多个头寸。更改存储为增量。因此,以第 1 天为例...
AC_ID | POS_ID | ASAT | VAL
1 | 1 | 2016-01-01 | 100
1 | 2 | 2016-01-01 | 200
2016 年 1 月 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])
我现在想做的是编写一个查询,它将为每个ASAT
提供每个AC_ID
的总值。如果不是增量存储机制,我可以使用
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
我改变了它的工作方式
虽然下面的答案有效,但它们的性能却非常糟糕(这不是作者的错!)为了得到可接受的结果(我需要亚秒级返回)我重构了表格以包含 end_date
柱子。此列在每次插入时都会更新,以设置该行的生命周期。如果一行没有替代条目,则结束日期设置为 9999-12-31。我上面的例子变成了……
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
然后我可以从接受的答案中删除第二个连接,并在内部连接中添加一个额外的子句。
SELECT
p1.AC_ID,
p1.ASAT,
SUM(p2.VAL) as totalValue
FROM
(SELECT DISTINCT AC_ID, ASAT FROM position) p1
INNER JOIN position p2 ON
p2.AC_ID = p1.AC_ID AND
p2.ASAT <= p1.ASAT AND
p2.END_DATE > p1.END_DATE
GROUP BY
p1.AC_ID,
p1.ASAT;
【问题讨论】:
如果给定日期没有值怎么办?你还想要那个日期吗? 最好不要,即我只对表中列出的日期感兴趣,但如果为了实现这一点,我必须填写其间的每个日期,那么这应该没什么大不了的.日期间隔可能只有 1 或 2 天。 澄清一下,任何日期都会有一个值,因为它将是它之前的最新不同头寸的总和。例外情况是,如果日期在任何位置之前存在,在这种情况下结果将为 null/0 【参考方案1】:这应该可以满足您的需求:
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
而言不是最新的行。
【讨论】:
它确实有效。谢谢你,但在我接受这个之前,我想知道这里是否有可以优化的东西。当我运行 SELECT AC_ID, ASAT, SUM(VAL) FROM POSITION GROUP BY AC_ID, ASAT ORDER BY ASAT DESC 时,在 300k 行的表上执行需要 0.117 秒。新的 SQL 给了我正确的答案,但针对同一组数据需要超过 5 分钟才能完成。我确实有关于 ac_id、pos_id、asat 的索引。想知道是否需要 2 次全表扫描。 可能还有一些其他的索引可以优化得更好,但是您的业务规则的性质将需要多次表扫描。如果这是另一个 RDBMS,您可能会从窗口函数中获得更好的性能,但它们在 mysql 中不可用。你也可以尝试一些使用变量的技巧(这就是很多人在 MySQL 中模仿窗口函数的方式),但我现在没有可用于测试的服务器,也没有想到这一点。跨度> 两个答案都是正确的。这个是性能最好的。尽管非常糟糕。我决定重构以减少查询时间。已将其记录为对原始帖子的编辑。【参考方案2】:这不是特别有效,但我认为它应该做你想要的:
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;
【讨论】:
感谢您的起点。不幸的是,如果有多个 AC_ID,这将不起作用。它只是将所有内容聚合在一行中(并且需要很长时间!) @NathPapadacis 。 . .糟糕,应该有一个group by
。这不会解决性能问题,但答案应该是正确的。
啊哈,差不多了。它只需要最后一项调整即可使其工作,即更改连接以检查ASAT
上的and p.ASAT <= aa.ASAT以上是关于SQL Group By 每个日期的总和并使用最大日期的主要内容,如果未能解决你的问题,请参考以下文章
使用多个 WHERE 子句和 GROUP BY 销售人员访问 SQL、聚合总和
具有日期范围条件的 Group By 和 SUM 的 sql