为啥这个 LEFT JOIN 会消除另一个表中没有任何内容的记录?

Posted

技术标签:

【中文标题】为啥这个 LEFT JOIN 会消除另一个表中没有任何内容的记录?【英文标题】:Why is this LEFT JOIN eliminating records with nothing in the other table?为什么这个 LEFT JOIN 会消除另一个表中没有任何内容的记录? 【发布时间】:2010-09-30 06:14:37 【问题描述】:

我有一个 mysql Left Join 问题。

我正在尝试加入三个表。

一个人表:

创建表人(
    id INT NOT NULL AUTO_INCREMENT,
    键入 ENUM('student', 'staff', 'guardian') NOT NULL,
    first_name CHAR(30) NOT NULL,
    姓氏 CHAR(30) 非空,
    性别 ENUM('m', 'f') NOT NULL,
    dob VARCHAR(30) 非空,
    主键(id)
);

学生表:

创建表学生 (
    id INT NOT NULL AUTO_INCREMENT,
    person_id INT NOT NULL,
    primary_guardian INT NOT NULL,
    secondary_guardian INT,
    join_date VARCHAR(30) 非空,
    status ENUM('current', 'graduated', 'expelled', 'other') NOT NULL,
    导师组 VARCHAR(30) 非空,
    year_group VARCHAR(30) 非空,
    主键(id),
    FOREIGN KEY (person_id) REFERENCES person(id) ON DELETE CASCADE,
    外键(primary_guardian)参考监护人(id),
    FOREIGN KEY (secondary_guardian) REFERENCES Guardian(id),
    外键(导师组)参考导师组(名称),
    FOREIGN KEY (year_group) REFERENCES year_group(name)
);

还有一个事件表:

CREATE TABLE 事件 (
    id INT NOT NULL AUTO_INCREMENT,
    学生 INT NOT NULL,
    员工 INT NOT NULL,
    监护人 INT NOT NULL,
    sent_home BOOLEAN NOT NULL,
    疾病类型 VARCHAR(255) 非空,
    action_taken VARCHAR(255) 非空,
    event_date DATETIME NOT NULL,
    主键(id),
    外键(学生)参考学生(id),
    外键(员工)参考员工(id),
    外键(监护人)参考监护人(id)
);

我要选择的是第 9 年每个学生的名字、姓氏和事件数量。

这是我对查询的最佳尝试:

选择 p.first_name, p.last_name, COUNT(i.student)
FROM person p, student s LEFT JOIN event i ON s.id = i.student
WHERE p.id = s.person_id AND s.year_group LIKE "%Year 9%";

但是,它会忽略任何没有发生事件的学生,这不是我想要的 - 他们应该显示但计数为 0。如果我删除左连接和计数,那么我会得到所有学生,如我所料。

我可能误解了左连接,但我认为它应该做的,基本上是我想要做的?

感谢您的帮助,

亚当

【问题讨论】:

【参考方案1】:

你所做的很好,你只是错过了 group by 子句

SELECT p.first_name, p.last_name, COUNT(i.student)
FROM person p, student s  LEFT JOIN incident i ON s.id = i.student 
WHERE p.id = s.person_id AND s.year_group LIKE "%Year 9%"
GROUP BY p.first_name, p.last_name;

这是一些测试数据

insert into person values(1, 'student', 'Alice', 'Foo', 'f','1970-01-01');
insert into person values(2, 'student', 'Bob', 'Bar', 'm','1970-01-01');

insert into student values(1,1,0,0,'', 'current','','Year 9');
insert into student values(2,2,0,0,'', 'current','','Year 9');

insert into incident values(1,1,0,0,0,'flu','chicken soup', '2008-01-08');

下面是添加了组 by 的查询的输出:

+------------+-----------+------------------+
| first_name | last_name | COUNT(i.student) |
+------------+-----------+------------------+
| Alice      | Foo       |                1 |
| Bob        | Bar       |                0 |
+------------+-----------+------------------+

您可以通过在 where 子句中创建连接子句并根据人员 ID 进行分组来进一步清理查询:

SELECT p.first_name, p.last_name, COUNT(i.student)
FROM person p
INNER JOIN student s ON(p.id = s.person_id)
LEFT JOIN incident i ON(s.id = i.student)
WHERE s.year_group LIKE "%Year 9%"
GROUP BY p.id;

【讨论】:

感谢 Paul 的课程 :) 重新阅读我的答案后,您完全正确 :) 对此表示赞同,因为这是迄今为止对该问题的最佳答案。 好的,非常感谢。快速提问,也许没关系,反正我下次会记得但为什么它需要 GROUP BY? count() 是一个聚合函数,这意味着它要么需要对结果集中的所有行进行操作,要么需要告诉它如何将这些行组合在一起。如果将常规列与聚合函数混合使用,则需要 GROUP BY 子句。【参考方案2】:

或者,您可以通过使用correlated subquery 来避免LEFT JOIN

SELECT
  p.first_name
  , p.last_name
  , (SELECT COUNT(*) FROM incident i WHERE i.student = s.id) 
FROM
  person p JOIN student s on s.person_id = p.id
WHERE
  s.year_group LIKE "%Year 9%"

【讨论】:

【参考方案3】:

您一开始使用的是不带 group by 的 count,并且您正在混合使用“where”和“on”语法进行连接。 试试这个:

SELECT p.first_name, p.last_name, COUNT(i.student)
FROM person p
JOIN student s on p.id = s.person
LEFT JOIN incident i ON s.id = i.student 
WHERE s.year_group LIKE "%Year 9%"
GROUP BY P.id;

【讨论】:

【参考方案4】:

这不是您正在寻找的左外连接吗?我可能混淆了我的术语?不会是第一次。但阿伦的回答会奏效。

【讨论】:

如果我没记错的话,LEFT JOIN 就是 LEFT OUTER JOIN。 (与 JOIN 或 INNER JOIN 对比。)

以上是关于为啥这个 LEFT JOIN 会消除另一个表中没有任何内容的记录?的主要内容,如果未能解决你的问题,请参考以下文章

left joinright join和inner join

left joinright join和inner join

oracle SQL left join()或full out join()根据键排除记录

SQL SERVER LEFT JOIN, INNER JOIN, RIGHT JOIN

使用 LEFT JOIN 删除

mysql,left join查询,数据变多了????