MYSQL group by 和内部连接

Posted

技术标签:

【中文标题】MYSQL group by 和内部连接【英文标题】:MYSQL group by and inner join 【发布时间】:2013-01-08 11:03:59 【问题描述】:

我有一个文章表,其中包含每天的文章浏览量。将创建一条新记录来保存每篇文章的每一天的计数。

下面的查询获取文章 ID 和总浏览量排名前 5 的文章 ID 的所有时间:

SELECT article_id, 
SUM(article_count) as cnt
FROM article_views
GROUP BY article_id
ORDER BY cnt DESC
LIMIT 5 

我还有一个单独的文章表,其中包含所有文章字段。我想修改上面的查询以加入文章表并为每个文章 ID 获取两个字段。我尝试在下面执行此操作,但计数返回错误:

SELECT article_views.article_id, SUM( article_views.article_count ) AS cnt, articles.article_title, articles.artcile_url
FROM article_views
INNER JOIN articles ON articles.article_id = article_views.article_id
GROUP BY article_views.article_id
ORDER BY cnt DESC
LIMIT 5

我不确定我到底做错了什么。我需要做一个子查询吗?

【问题讨论】:

【参考方案1】:

articles.article_title, articles.artcile_url 添加到GROUP BY 子句中:

SELECT 
  article_views.article_id, 
  articles.article_title, 
  articles.artcile_url,
  SUM( article_views.article_count ) AS cnt
FROM article_views
INNER JOIN articles ON articles.article_id = article_views.article_id
GROUP BY article_views.article_id,   
         articles.article_title, 
         articles.artcile_url
ORDER BY cnt DESC
LIMIT 5;

您没有得到正确结果集的原因是,当您选择未包含在 GROUP BYSELECT 子句中的聚合函数中的行时,mysql 会选择随机值。

【讨论】:

令我印象深刻的是,您似乎对数据结构的了解比问题中介绍的要多得多。就一点。 MySQL 不会选择 random 值。它选择一个 任意 值。 Random 表示可以选择任何值。事实上,它几乎总是第一条记录中的值——但你不能依赖这个事实。 如果article 表中恰好有行匹配article_idarticle_titlearticle_url,(即不保证这三者的组合是唯一的),那么与原始查询相比,此查询返回的计数仍受“关闭”的影响。 (这确实让人想知道为什么“article”表中名为“article_id”的列中会有任何重复值。)【参考方案2】:

您正在使用称为隐藏列的 MySQL (mis) 功能,因为文章标题不在 group by 中。但是,这可能会也可能不会导致您的问题。

如果计数错误,那么我认为您在文章表中有重复的article_id。您可以通过以下方式检查:

select article_id, count(*) as cnt
from articles
group by article_id
having cnt > 1

如果出现任何问题,那就是你的问题。如果他们都有不同的标题,那么按标题分组(如 Mahmoud 建议的那样)将解决问题。

如果没有,修复它的一种方法如下:

SELECT article_views.article_id, SUM( article_views.article_count ) AS cnt, articles.article_title, articles.artcile_url
FROM article_views INNER JOIN
     (select a.* from articles group by article_id) articles
     ON articles.article_id = article_views.article_id
GROUP BY article_views.article_id
ORDER BY cnt DESC
LIMIT 5

这会为文章选择一个任意标题。

【讨论】:

使用这种方法,cnt 值不会因article_id 值重复出现在articles 表中而被夸大;以这种方式隔离查询似乎更好。【参考方案3】:

您的查询对我来说基本上是正确的......

cnt 返回的值将取决于article_id 列在articles 表中的唯一性。我们假设它是主键,并且没有架构定义,这只是一个假设。)

此外,我们可能假设表之间存在外键,也就是说,articles_view 表中没有与行上的 article_id 值不匹配的 article_id 值来自articles 表。


要检查“孤儿” article_id 值,请运行如下查询:

SELECT v.article_id
  FROM articles_view v
  LEFT
  JOIN articles a
    ON a.article_id = v.article_id
 WHERE a.article_id IS NULL

要检查文章中的“重复”article_id 值,请运行如下查询:

SELECT a.article_id
  FROM articles a
 GROUP BY a.article_id
HAVING COUNT(1) > 1 

如果这些查询中的任何一个返回行,这可能是对您观察到的行为的解释。

【讨论】:

以上是关于MYSQL group by 和内部连接的主要内容,如果未能解决你的问题,请参考以下文章

如何通过内部连接使用 group by

oracle sql中的group by表达式的内部连接[重复]

mysql连接3个表与group by group by

MySQL group by 左连接

MySQL 查询优化与 group by 和 order by rand

如何使用 GROUP BY 连接 MySQL 中的字符串?