在单个 SELECT 中获取条件计数和条件 DISTINCT 计数

Get conditional count and conditional DISTINCT count in a single SELECT

我有 3 个 table:

TABLE: session_log
 user_id | device | logged_on
---------|--------|---------------------
 1       | web    | 2022-01-01 12:43:25
 1       | web    | 2022-01-01 13:33:32
 2       | mobile | 2022-01-01 18:20:18
 1       | mobile | 2022-01-01 08:22:41
 2       | web    | 2022-01-01 09:10:16
 3       | web    | 2022-01-01 07:52:21
 1       | web    | 2022-01-02 10:42:14

TABLE: standard_users
 user_id | username
---------|-----------
 1       | adam
 2       | jennifer

TABLE: admin_users
 user_id | username
---------|-----------
 3       | george

我想计算每天和每种设备类型(移动设备或网络)的唯一非管理员(标准)用户的数量。另一个要注意的是,如果用户在同一天同时登录 Web 和移动设备,我仍然希望将它们包括在两种设备类型的计数中,但当天每种设备的计数不超过一次。

从上面的 table 记录中,我想得到的结果计数应该如下所示:

 login_date | mobile_count | web_count
------------|--------------|-----------
 2022-01-01 | 2            | 2
 2022-01-02 | 0            | 1

目前我有一个查询没有考虑唯一用户条件,我很难弄清楚如何修改它以同时考虑每个计数的唯一用户 ID。

SELECT
    s.logged_on::date AS login_date,
    sum(CASE WHEN s.device = 'mobile' THEN 1 ELSE 0 END) AS mobile_count,
    sum(CASE WHEN s.device = 'web' THEN 1 ELSE 0 END) AS web_count,
FROM session_log s
LEFT JOIN standard_users su ON su.user_id = s.user_id 
WHERE su.user_id IS NOT NULL
GROUP BY login_date;

我目前的上述查询在上面的示例数据上 运行 时为 web_count 提出了 3,因为它没有计算 user_id 的唯一性所以它在 1 月 1 日两次计算 user_id 为 1 的记录。

有没有办法修改我的查询,以便在执行每个总和时也考虑 user_id 的唯一性?

使用聚合 FILTER 子句。然后你可以将你的计数与 DISTINCT:

结合起来
SELECT s.logged_on::date AS login_date
     , count(*)                FILTER (WHERE s.device = 'mobile') AS mobile_count
     , count(DISTINCT user_id) FILTER (WHERE s.device = 'web') AS web_count
FROM   session_log s
JOIN   standard_users su USING (user_id)
GROUP  BY login_date;

参见:

  • Aggregate columns with additional (distinct) filters

我还用 LEFT JOINIS NOT NULL 简化了你扭曲的公式。归结为一个普通的 JOIN.

如果 session_log.user_idstandard_users.user_id 之间的引用完整性是通过 FK 约束强制执行的,并且 standard_users.user_id 被定义为 UNIQUE 或 PK - 看起来很合理 - 你可以删除 JOIN 完全:

SELECT logged_on::date AS login_date
     , count(*)                FILTER (WHERE device = 'mobile') AS mobile_count
     , count(DISTINCT user_id) FILTER (WHERE device = 'web') AS web_count
FROM   session_log
GROUP  BY 1;