SQL:联合或自加入

Posted

技术标签:

【中文标题】SQL:联合或自加入【英文标题】:SQL: Union or Self Join 【发布时间】:2017-03-25 19:07:34 【问题描述】:

我有一个简单的表:user(id, date, task)

任务字段包含“下载”或“上传”

我想计算每天执行每个操作的用户数量。

输出:日期、下载用户数、上传用户数

我第一次遇到在select的聚合计数函数中使用子查询的问题,所以我想我应该在这里使用自连接来分解“任务”列中的数据。

我以为我可以为每个案例创建表格,然后将它们组合起来并计数,但我无法完成这个:

选择 id、日期、任务作为 task_download 来自用户 WHERE 任务 = '下载'

选择 id、日期、任务作为 task_upload 来自用户 WHERE 任务 = '上传'

【问题讨论】:

【参考方案1】:
select  `date`, 
COUNT( distinct CASE WHEN task = 'download' then id end ) 'download', 
COUNT( distinct CASE WHEN task = 'upload' then id end ) 'upload'
from user
group by  `date`

【讨论】:

【参考方案2】:

我会说,既不是也不是。只需这样的查询即可完成工作:

select `date`, 
    count(distinct case when task = 'download' then id else null end) as downloads, 
    count(distinct case when task = 'upload' then id else null end) as uploads
from user
where  task in ('download', 'upload')
group by `date`

假设,date 是仅包含日期部分而不包含完整时间戳的列,id 是用户 ID。您可以在聚合函数中使用 distinct 关键字,这就是我在这里所做的。

为了让这个查询以适当的速度运行,我建议在task,date上使用索引

但是,如果 date 包含完整的时间戳(即包括时间部分),您希望以不同方式进行分组:

select `date`, 
    count(distinct case when task = 'download' then id else null end) as downloads, 
    count(distinct case when task = 'upload' then id else null end) as uploads
from user
where  task in ('download', 'upload')
group by date(`date`)

【讨论】:

请注意,这里计算的是上传和下载的数量,而不是执行这些操作的用户数量(例如,如果一个用户一天执行 17 次下载和 4 次上传,并且该用户是唯一执行这些操作的用户)当天执行了一项任务,此查询将返回 17 和 4 的计数,而不是 1 和 1。。 @spencer7593 它返回每个用户每天的上传和下载次数。不是用户数量。请参阅group 声明。我假设“id”是用户 id(问题中没有明确说明)。 规范有点模棱两可(没有样本数据,没有预期的输出),但我认为要求是返回 users "# of users 谁下载了”,而不是“下载次数”。我解释这意味着一个用户在一个日期执行八次下载应该算作一个用户,而不是八个用户。 (我不认为id 需要成为结果或分组依据的一部分,如果那是代理主键,但同样,规范有点模棱两可。) 确实是这样,我看错了。感谢您清除这一点,我会更新我的答案 @Psi 这很有意义,如果我想订购我上个月的日期,我是否只需添加以下内容:按日期 DESC 订购,限制 30;【参考方案3】:

您可以使用子查询来做到这一点,例如:

SELECT `date` AS `day`,
(SELECT COUNT(*) FROM activity WHERE date = day AND activity = 'upload') AS upload_count,
(SELECT COUNT(*) FROM activity WHERE date = day AND activity = 'download') AS download_count
FROM activity
GROUP BY date;

这是SQL Fiddle

【讨论】:

返回给定日期的下载计数,不一定是用户数。即,如果一个用户在同一日期执行了四次下载......应该算作总数的四次,还是算作一次? (规范有点模棱两可,但我认为它要求计算用户数量...“# of users whodownload”。)【参考方案4】:

首先按日期和任务计算不同的用户,然后根据每个任务按日期汇总用户。

select date,
       sum(case when task = 'upload' then num_users else 0 end) as "upload",
       sum(case when task = 'download' then num_users else 0 end) as "download"
from  (       
       select   date, task, count(distinct id) num_users
       from     usert
       group by date, task
      ) x
group by date
;

在这里查看:http://rextester.com/ZACFB64945

【讨论】:

【参考方案5】:

如果您想要不同的用户,那么建议count(distinct)

SELECT date, 
       COUNT(DISTINCT CASE WHEN task = 'upload' THEN userid END) as uploads,
       COUNT(DISTINCT CASE WHEN task = 'download' THEN userid END) as downloads
FROM user
GROUP BY date
ORDER BY date;

如果你想要不同的动作,那么你可以这样做:

SELECT date, 
       SUM( (task = 'upload')::int ) as uploads,
       SUM( (task = 'download')::int) as downloads
FROM user
GROUP BY date
ORDER BY date;

这使用方便的 Postgres 简写来计算布尔表达式。

【讨论】:

【参考方案6】:

我会使用条件聚合。

获取在给定日期执行至少一次上传的用户数的计数(但仅将该用户在该日期的计数增加一,即使该用户执行了更多而不是在同一日期上传),我们可以使用COUNT(DISTINCT user) 表达式。

要获得上传总数的计数,我们可以使用 COUNT 或 SUM。

SELECT DATE(t.date) AS `date`
     , COUNT(DISTINCT IF(t.task='upload'  ,t.user,NULL)) AS cnt_users_who_uploaded
     , COUNT(DISTINCT IF(t.task='download',t.user,NULL)) AS cnt_users_who_downloaded
     , SUM(IF(t.task='upload'  ,1,0))                    AS cnt_uploads
     , SUM(IF(t.task='download',1,0))                    AS cnt_downloads
  FROM user t
 GROUP BY DATE(t.date)
 ORDER BY DATE(t.date)

注意:对于没有行且date 未出现在表中的日期,这不会返回零计数。

【讨论】:

以上是关于SQL:联合或自加入的主要内容,如果未能解决你的问题,请参考以下文章

使用 ADODB 记录集执行联合更新​​查询

4. SQL — 表的联合

SQL两张表联合查询

sql联合查询语句(两张表)

在联合之后只返回一条记录

sql如何一对多联合查询