为啥这个 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()根据键排除记录