mysql使用count的多个左连接

时间:2016-05-22 23:34:13

标签: mysql join count coalesce

我一直在研究这个问题几个小时,我提出的最好的代码就是我在一个问题上发现的一个例子。我经历了几个派生,但以下是唯一返回正确数据的查询,问题是只返回139行(超过2分钟)才能返回30行数据。我卡住了。 (life_p是'喜欢'

SELECT
  logos.id,
  logos.in_gallery,
  logos.active,
  logos.pubpriv,
  logos.logo_name,
  logos.logo_image,
  coalesce(cc.Count, 0) as CommentCount,
  coalesce(lc.Count, 0) as LikeCount
FROM logos

left outer join(
  select comments.logo_id, count( * ) as Count from comments group by comments.logo_id
) cc on cc.logo_id = logos.id

left outer join(
  select life_p.logo_id, count( * ) as Count from life_p group by life_p.logo_id
) lc on lc.logo_id = logos.id

WHERE logos.active = '1'
  AND logos.pubpriv = '0'
GROUP BY logos.id
ORDER BY logos.in_gallery desc
LIMIT 0, 30

我不确定这是错的。如果我这样做,那么意味着要删除coalece和其中一个连接:

SELECT
  logos.id,
  logos.in_gallery,
  logos.active,
  logos.pubpriv,
  logos.logo_name,
  logos.logo_image,
  count( * ) as lc
FROM logos

left join life_p on life_p.logo_id = logos.id

WHERE logos.active = '1'
  AND logos.pubpriv = '0'
GROUP BY logos.id
ORDER BY logos.in_gallery desc
LIMIT 0, 30

运行时间不到半秒(2-300毫秒)....

以下是解释的链接:https://logopond.com/img/explain.png

4 个答案:

答案 0 :(得分:2)

MySQL有一个特殊的怪癖,它允许group by子句不列出所有非聚合列。这不是一件好事,您应该始终在group by子句中指定所有非聚合列。

注意,当计算连接表时,知道COUNT()函数忽略NULL是很有用的,因此对于可能发生NULL的LEFT JOIN,不要使用COUNT(*),而是使用内部的列联接表,只计算该表中的行。从这些点我建议以下查询结构。

SELECT
        logos.id
      , logos.in_gallery
      , logos.active
      , logos.pubpriv
      , logos.logo_name
      , logos.logo_image
      , COALESCE(COUNT(cc.logo_id), 0) AS CommentCount
      , COALESCE(COUNT(lc.logo_id), 0)   AS LikeCount
FROM logos
        LEFT OUTER JOIN comments cc ON cc.logo_id = logos.id
        LEFT OUTER JOIN life_p lc ON lc.logo_id = logos.id
WHERE logos.active = '1'
AND logos.pubpriv = '0'
GROUP BY
        logos.id
      , logos.in_gallery
      , logos.active
      , logos.pubpriv
      , logos.logo_name
      , logos.logo_image
ORDER BY logos.in_gallery DESC
LIMIT 0, 30

如果您仍然遇到性能问题,请使用执行计划并考虑添加适合的索引。

答案 1 :(得分:1)

您可以在加入字段上创建一些索引:

ALTER TABLE table ADD INDEX idx__tableName__fieldNamefield

在你的情况下将是:

ALTER TABLE cc ADD INDEX idx__cc__logo_id(logo_id);

答案 2 :(得分:0)

我真的不喜欢它,因为我总是读到子查询很糟糕,并且连接在压力下表现更好,但在这种特殊情况下,子查询似乎是在半秒内持续提取正确数据的唯一方法。感谢大家的建议。

SELECT 
    logos.id,
    logos.in_gallery,
    logos.active,
    logos.pubpriv,
    logos.logo_name,
    logos.logo_image,
(Select COUNT(comments.logo_id) FROM comments
    WHERE comments.logo_id = logos.id) AS coms,
(Select COUNT(life_p.logo_id) FROM life_p 
   WHERE life_p.logo_id = logos.id) AS floats 

FROM logos
WHERE logos.active = '1' AND logos.pubpriv = '0'
ORDER BY logos.in_gallery desc
LIMIT ". $start .",". $pageSize ."

答案 3 :(得分:0)

您还可以创建映射表以加快查询速度:

CREATE TABLE mapping_comments AS
SELECT
    comments.logo_id,
    count(*) AS Count
FROM
    comments
GROUP BY
    comments.logo_id
) cc ON cc.logo_id = logos.id

然后更改您的代码

 left outer join(

应该成为

inner join mapping_comments as mp on mp.logo_id =cc.id

然后,每次向cc表添加新注释时,您需要更新映射表,或者您可以创建存储过程以在cc表更改时自动执行