多个连接上的多个COUNT会神秘地增加计数

时间:2014-03-21 16:33:26

标签: mysql sql

我有三张桌子

CREATE TABLE posts (
    id    SMALLINT     UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    slug  VARCHAR(64)  NOT NULL UNIQUE,
    ...
) ENGINE InnoDB ;

CREATE TABLE comments (
    id    INT        UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    post  SMALLINT   UNSIGNED REFERENCES posts(id) ON DELETE SET NULL,
    ...
) ENGINE InnoDB ;

CREATE TABLE visits (
    id    INT        UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    post  SMALLINT   UNSIGNED REFERENCES posts(id) ON DELETE SET NULL
    ...
) ENGINE InnoDB ;

这个查询

SELECT
    posts.*,
    DATE_FORMAT(posts.posted, '%W, %M %e, %Y') as d,
    COUNT(visits.post) as views,
    COUNT(comments.post) as comments
FROM posts
LEFT JOIN visits ON posts.id = visits.post
LEFT JOIN comments ON posts.id = comments.post
WHERE
    posts.slug = '$slug'
    AND posts.published = 1
    AND posts.visible = 1 ;

不幸的是,viewscomments最终都包含相同的值:评论数量乘以访问次数。任何一个计数/连接对都可以自己工作,但是一旦我将它们放在一起,我就会得到这个结果。那是为什么?

我最后尝试添加GROUP BY posts.id,但没有区别。

由于slug列包含唯一值,因此此查询应该只有一个结果。

2 个答案:

答案 0 :(得分:0)

这不是“神秘的”。这是加入工作的方式。如果帖子有3次访问和4条评论,那么在聚合之前,您将从查询逻辑中获得12行。

在您的查询中,您可以使用count(distinct)

解决此问题
SELECT
    posts.*,
    DATE_FORMAT(posts.posted, '%W, %M %e, %Y') as d,
    COUNT(distinct visits.id) as views,
    COUNT(distinct comments.id) as comments
    . . .

只要您没有针对特定帖子进行数百次访问和评论,这将有效。在这些情况下,您需要在进行连接之前预先聚合结果,或者在每个帖子的子查询中进行聚合。

答案 1 :(得分:0)

您想要在查询中添加group by post.id子句。

当“on子句”匹配时,连接会导致“笛卡尔积”出现。 (左或右连接允许'缺失'ons返回空值而不是)。分组将告诉SQL采用该笛卡尔积,但将结果与匹配值组合在一起限制为单行。