使用JOIN表的错误MySQL查询结果

时间:2019-03-04 15:32:54

标签: mysql

以下MySQL查询假设按帖子的视图+评分+提交日期以升序对帖子进行排名:

select
    cat                    ,
    p.id                   ,
    title                  ,
    p.date                 ,
    shares                 ,
    source                 ,
    cat                    ,
    count(v.post_id) views ,
    sum(r.ilike)     rating,
    r.module               ,
    r.module_id            ,
    @Rank := @Rank + 1 AS Rank
from
    posts p
    JOIN
        rates r
        on
            r.module_id  = p.id
            AND r.module = 'posts'
    JOIN
        posts_views v
        on
            v.post_id = p.id
WHERE
    p.date     <= UNIX_TIMESTAMP(NOW())
    AND p.state = '3'
    AND
    (
        p.cat NOT REGEXP '[[:<:]]15[[:>:]]'
    )
GROUP BY
    r.module_id
ORDER BY
    rating DESC       ,
    views DESC        ,
    p.date ASC LIMIT 0,     10

给出以下结果: query result screen shot

结果有3个问题:

  1. 视图列的值加倍
  2. 评分列值正在复制观看次数'值
  3. NULL中的排名

1 个答案:

答案 0 :(得分:0)

该查询正在生成半笛卡尔积。通过使用r中的多个匹配行和v中的多个匹配行,这些行将被匹配在一起,从而夸大了ratingviews的结果。如果我们删除GROUP BY和聚合函数,并返回详细信息行,则可以观察到“重复”行,这些行导致视图计数增加一倍,三倍……

对此的一种解决方法是,通过在嵌入式视图中至少对一个子表进行预聚合来避免使用笛卡尔积。然后,我们将派生表连接到posts表,以将汇总表添加到外部查询。

viewsrates中没有匹配的行时,我们可能想考虑使用外部联接来处理条件,因此对于没有任何视图的帖子,我们可以返回零计数

将用户定义的变量初始化为单独的语句,或者在内联视图中初始化。

此外,我们希望对所有列引用进行限定,以帮助将来的读者(不要强迫他们查看表定义以找出像cat这样的列或titlesource来自),并避免在将来向同一个引用的表中添加相同名称的列时,查询因“歧义列”错误而中断。查询。

我建议这样:

SELECT p.cat
     , p.id
     , p.title
     , p.date
     , p.shares
     , p.source
     , p.cat
     , IFNULL(v.cnt_views,0)  AS views
     , r.tot_rating           AS rating
     , r.module
     , r.module_id
     , @Rank := @Rank + 1     AS Rank

  FROM ( SELECT @Rank := 0 ) i

 CROSS
  JOIN posts p

  LEFT
  JOIN ( SELECT ra.module_id
              , MAX(ra.module)   AS module
              , SUM(ra.ilike)    AS tot_rating
           FROM rates ra
          WHERE ra.module = 'posts'
          GROUP
             BY ra.module_id
       ) r
    ON r.module_id = p.id

  LEFT
  JOIN ( SELECT pv.post_id
              , SUM(1)    AS cnt_views
           FROM posts_views pv
          GROUP
             BY pv.post_id
       ) v
    ON v.post_id = p.id

 WHERE p.date <= UNIX_TIMESTAMP(NOW())
   AND p.state = '3'
   AND p.cat NOT REGEXP '[[:<:]]15[[:>:]]'

 ORDER
    BY r.tot_rating DESC
     , v.cnt_views  DESC
     , p.date ASC
 LIMIT 0, 10