我有一个表(PostgreSQL 9.6),其中包含 260 万多个与帐户标识符关联的带时间戳的行,对于任何给定的标识符,我想在单个查询中计算出现的总数以及今天的出现次数。
作为参考,这是这个问题中描述的同一张表,但我在这里对其进行了简化以关注这个特定问题:
CREATE TABLE account_test
(
id integer NOT NULL PRIMARY KEY
);
CREATE TABLE log_test
(
account integer NOT NULL REFERENCES account_test(id),
event_time timestamp with time zone NOT NULL DEFAULT now()
);
CREATE INDEX account_test_idx ON log_test USING btree (account,event_time);
INSERT INTO account_test VALUES (1);
INSERT INTO account_test VALUES (2);
INSERT INTO log_test VALUES (1,'2018-01-01');
INSERT INTO log_test VALUES (1,'2018-01-02');
INSERT INTO log_test VALUES (1,'2018-01-03');
INSERT INTO log_test VALUES (1,now());
INSERT INTO log_test VALUES (1,now());
INSERT INTO log_test VALUES (2,'2018-01-01');
INSERT INTO log_test VALUES (2,'2018-01-02');
INSERT INTO log_test VALUES (2,now());
这是我最初的尝试,由于以下原因,每日计数和总计数都会产生相同的数字GROUP BY
:
SELECT a.id,COUNT(d) AS daily,COUNT(t) AS total FROM account_test a
JOIN log_test d ON a.id=d.account AND d.event_time > now() - interval '1 day'
JOIN log_test t ON a.id=t.account
WHERE a.id=1 GROUP BY a.id;
id | daily | total
----+-------+-------
1 | 10 | 10
(1 row)
我正在寻找的结果是:
id | daily | total
----+-------+-------
1 | 2 | 5
(1 row)
具体来说,这个丑陋的查询的结果:
SELECT qd.id,qd.daily,qt.total FROM
(
SELECT a.id,COUNT(d) AS daily FROM account_test a
JOIN log_test d ON a.id=d.account AND d.event_time > now() - interval '1 day'
WHERE a.id=1 GROUP BY a.id
) qd,
(
SELECT a.id,COUNT(t) AS total FROM account_test a
JOIN log_test t ON a.id=t.account
WHERE a.id=1 GROUP BY a.id
) qt;
我意识到这可能是一个垒球问题,但在这种情况下,我的 SQL 直觉让我失望了,我怀疑可能有一些巧妙的技巧可以消除额外的JOIN
.
我相信使用
SUM
+CASE
表达式会起作用,因为它CASE
使您能够进行“选择性计数”。或者您可以使用较新FILTER
的:使用您问题中的数据集,这给出了以下结果:
这种方法不使用 FILTER