同一列上的多个 COUNT

val*_*tis 1 postgresql count postgresql-9.6

我有一个表 (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());
Run Code Online (Sandbox Code Playgroud)

这是我的初步尝试,由于以下原因,每日和总计数产生相同的数字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)
Run Code Online (Sandbox Code Playgroud)

我正在寻找的结果是:

 id | daily | total
----+-------+-------
  1 |     2 |     5
(1 row)
Run Code Online (Sandbox Code Playgroud)

具体来说,这个丑陋的查询的结果:

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;
Run Code Online (Sandbox Code Playgroud)

我意识到这可能是一个垒球问题,但在这种情况下,我的 SQL 直觉让我失望,我怀疑可能有一些聪明的技巧可以消除额外的JOIN.

Glo*_*del 5

我相信使用SUM+CASE表达式会起作用,因为它CASE使您能够进行“选择性计数”。或者您可以使用较新的FILTER

SELECT a.id,
  count(*) FILTER (WHERE d.event_time > now() - interval '1 day') AS daily,      
  count(*) AS total
FROM account_test a 
JOIN log_test d ON a.id=d.account
GROUP BY a.id;
Run Code Online (Sandbox Code Playgroud)

使用您问题中的数据集,结果如下:

 id | daily | total
----+-------+-------
  2 |    1  |    3
  1 |    2  |    5
Run Code Online (Sandbox Code Playgroud)