MySQL JOIN 独立于 WHERE 子句

MySQL JOIN independent of WHERE clause

我有两个表:

debates
----------------------
id | name
----------------------
 1 | why is that?
 2 | why is the other?

opinions
---------------------
id | debate | opinion
---------------------
 1 | 1      | because
 2 | 1      | NULL

如果我使用 left join ON opinions.debate = debates.id 我可以得到两个辩论 (1 , 2).

如果我在 left join WHERE (opinions.opinion ! = '' AND opinions.opinion IS NOT NULL) 我只得到 id = 1

的辩论

如何获得两条记录但仍然保持条件,因为我用它来统计记录?

例如。查询:

SELECT 

debates.id,
COUNT(opinions.id) AS total_opinions

FROM debates

LEFT JOIN 
`opinions` ON `opinions`.`debate` = `debates`.`id` 

WHERE 
`opinions`.`opinion` IS NOT NULL AND `opinions`.`opinion` != '' 

GROUP BY 
`debates`.`id`

应该return:

debates
-------------------
id | total_opinions
-------------------
 1 | 1
 2 | 0

WHERE子句的谓词移动到ON:

SELECT debates.id,
       COUNT(opinions.id) AS total_opinions    
FROM debates    
LEFT JOIN `opinions` 
ON `opinions`.`debate` = `debates`.`id`  AND
   `opinions`.`opinion` IS NOT NULL AND 
   `opinions`.`opinion` != ''     
GROUP BY `debates`.`id`

此查询将获取 debates 所有 条记录,并加入满足 [=12= 指定条件的 opinions 的记录] 谓词。如果没有匹配 NULL 返回,COUNT 聚合函数将忽略它。

Demo here

LEFT OUTER JOIN 的目的是允许列出一个 table 的所有行,即使这些行未在另一个 table 中引用,如果这些条件存在,您将获得 NULL 值来自另一个 table。因此,即使其中一些没有意见,要获得所有辩论,您可以这样做:

FROM debates
LEFT OUTER JOIN `opinions` ON `opinions`.`debate` = `debates`.`id` 

当辩论中没有意见时,意见中数据的所有列位置 table 都将为 NULL

如果您正在计算意见的数量,那么请务必注意,如果值为 NON NULL,COUNT() 函数只会递增 1。因此,以下将自动计算正确的意见数量,而无需过滤掉 NULL。

SELECT 
      debates.id
    , COUNT(opinions.id) AS total_opinions
FROM debates
LEFT OUTER JOIN `opinions` ON `opinions`.`debate` = `debates`.`id` 
GROUP BY
      debates.id

现在,如果您确实在意见 table 中有行,其中意见是一个空白字符串,您可以这样做:

SELECT 
      debates.id
    , COUNT(opinions.id) AS total_opinions
FROM debates
LEFT OUTER JOIN `opinions` ON `opinions`.`debate` = `debates`.`id` 
                          AND `opinions`.`opinion` <> '' 
GROUP BY
      debates.id

或:

SELECT 
      debates.id
    , COUNT(case when `opinions`.`opinion` <> '' then opinions.id end) AS total_opinions
FROM debates
LEFT OUTER JOIN `opinions` ON `opinions`.`debate` = `debates`.`id` 
GROUP BY
      debates.id