我有一个表,用于存储一段时间内添加到同一数据库中不同表的记录数的统计信息。
这是统计表的简化架构。
+------+-------------+---------+-------+--------+
| id | programId | start | end | count |
+------+-------------+---------+-------+--------+
IRL 表包含使用中等复杂查询计算的多个计数和日期。每个programId
都将存在于表中,其end
值为null
,这就是给定程序的统计信息的存储位置。运行 cron 作业以定期关闭时间片(即将 end
列设置为有效日期)。下次更新统计数据时,将使用 start
值创建新的程序行,并将 end
设置为 null。
这是消息表的简化架构,它包含统计表中汇总的数据。该表保存统计表中汇总的数据。
+------+-------------+------------+--------+
| id | messageId | programId | time |
+------+-------------+------------+--------+
我想通过单个查询更新所有程序消息计数。以下查询不起作用,因为 where 语句中不允许使用聚合函数 max
。另外,我还阅读了有关在 where 子句中使用 coalesce
函数的警告。此外,在单个查询中反复连接相同的两个表似乎是错误。我不是 sql 专家(显然),但这个查询给我带来了各种危险信号 - 即使它有效,我也会在这里发帖寻找改进它的方法。
UPDATE stats a
INNER JOIN messages b on a.programId = b.programId
SET a.numberReceived =
( SELECT COUNT(c.id)
FROM messages c LEFT JOIN stats d ON c.programId = d.programId
WHERE c.datetime >= coalesce(max(d.end), '1970-01-01 00:00:01)
)
WHERE end IS NULL
逻辑是只计算最后一个关闭时间片之后到达的消息(如果存在),否则计算给定程序的所有消息(即,如果这是程序第一次出现在统计表中)。
我尝试将此计数逻辑添加到触发器中,但是,计算的统计数据比这更多(这是一个简化的示例),并且一次将许多行插入到消息表中。结果是,在 after insert
触发器中包含此逻辑会导致事务错误,从而导致插入失败。
我知道我可以通过循环程序并对数据库发出许多 sql 语句来以编程方式完成此操作,但我认为这可以在单个语句中完成。
最佳答案
方法如下:
UPDATE
stats a
INNER JOIN
(
SELECT m.programId, count(*) as cnt
FROM
messages m
LEFT JOIN
(
SELECT programId,max(end) as end
FROM
stats
WHERE end IS NOT NULL
GROUP BY programId
)as s
ON m.programId=s.programId
WHERE (s.end IS NULL OR m.datetime > s.end)
GROUP BY m.programId
) b
ON a.programId = b.programId
SET a.numberReceived = b.cnt
WHERE a.end IS NULL
您需要一个子查询s
来获取每个程序的最后结束日期,然后使用它来过滤结束日期之后的所有新消息。最后使用子查询b
来按programId统计新消息,然后您可以通过一个简单的更新子句进行更新。
关于mysql - 如何在单个查询中使用联接和聚合函数更新表中的多行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26436057/