MySQL 三表链式左连接,按最后一个表过滤
Posted
技术标签:
【中文标题】MySQL 三表链式左连接,按最后一个表过滤【英文标题】:MySQL three table chained left join, filtering by last table 【发布时间】:2015-06-13 13:24:48 【问题描述】:我试图找到一种有效的方法来过滤掉由链式三个表左连接产生的结果集,其中第二个表连接将考虑第三个表的属性。
小提琴:http://sqlfiddle.com/#!2/e319e/2/0
一个简化的示例是三个表之间的连接:Post、Comment 和 Author。帖子可以有 0..N 个由作者撰写的 cmets。我想获取仅由活跃作者撰写的所有帖子 + 活跃评论的列表。
考虑以下数据:
发帖:
| id | title |
|----|-------------|
| 1 | First post |
| 2 | Second post |
| 3 | Third post |
| 4 | Forth post |
作者:
| id | title | is_active |
|----|------------------------|-----------|
| 1 | First author | 1 |
| 2 | Second author | 1 |
| 3 | Third author | 1 |
| 4 | Fourth inactive author | 0 |
| 5 | Fifth inactive author | 0 |
评论:
| id | post_id | author_id | title | is_active |
|----|---------|-----------|------------------------|-----------|
| 1 | 1 | 1 | First comment | 1 |
| 2 | 2 | 1 | Second comment | 1 |
| 3 | 1 | 2 | Third comment | 1 |
| 4 | 2 | 4 | Fourth comment | 1 |
| 5 | 2 | 5 | Fifth inactive comment | 0 |
| 6 | 2 | 3 | Sixth inactive comment | 0 |
| 7 | 4 | 4 | Seventh comment | 1 |
现在执行简单的过滤查询:
SELECT
p.id post_id, p.title post_title,
c.id as comment_id, c.title comment, c.is_active active_comment,
a.id author_id, a.title author, a.is_active active_author
FROM Post p
LEFT JOIN Comment c ON c.post_id = p.id AND c.is_active = 1
LEFT JOIN Author a ON a.id = c.author_id AND a.is_active = 1
ORDER BY p.id;
为我们带来以下结果集:
| id | title | id | title | is_active | id | title | is_active |
|----|-------------|--------|-----------------|-----------|--------|---------------|-----------|
| 1 | First post | 1 | First comment | 1 | 1 | First author | 1 |
| 1 | First post | 3 | Third comment | 1 | 2 | Second author | 1 |
| 2 | Second post | 2 | Second comment | 1 | 1 | First author | 1 |
| 2 | Second post | 4 | Fourth comment | 1 | (null) | (null) | (null) |
| 3 | Third post | (null) | (null) | (null) | (null) | (null) | (null) |
| 4 | Forth post | 7 | Seventh comment | 1 | (null) | (null) | (null) |
有两个应该被省略的 cmets - “第四条评论”和“第七条评论”,它们是由不活跃的作者编写的。
我认为唯一可行的方法是为 Comment 添加 JOIN 条件
AND c.id IN (SELECT id FROM Author WHERE is_active = 1)
这会产生正确的结果集,但我想这不是最优的。但是我找不到任何其他可行的解决方案。有没有办法以某种方式优化它?谢谢!
【问题讨论】:
我认为您的数据完整性存在问题。您有 inactive 作者编写的 active cmets?当作者被停用时,评论不应该也被停用吗?注意:你还有一个好问题;我只是认为您可能需要在此查询之外进行修复。 你为什么想要没有任何活动的“第三篇文章”? Gordon,正如我所写,这是一个简化的示例 - 真实场景略有不同。但是,即使在这种情况下,我也会说对不活跃的作者进行积极的评论是有意义的——比如作者被暂时停职一周,但我们不想立即停用他的 cmets。不过感谢您的评论! Tim,因为主要是需要所有帖子的列表 + cmets。 【参考方案1】:我想你想要这个from
子句:
FROM Post p LEFT JOIN
(Comment c JOIN
Author a
ON a.id = c.author_id AND a.is_active = 1 and c.is_active = 1
)
ON c.post_id = p.id
不过,正如我在评论中提到的,您可能希望停用不活跃作者的 cmets。这将涉及触发器或存储过程。
哦,你很有礼貌地提出了一个 SQL Fiddle,所以here 它正在工作。
【讨论】:
@KlausK 。 . .我希望此查询与您的查询具有相同的执行时间(两个版本执行基本相同的连接)。您的问题是关于功能而不是性能。【参考方案2】:如果你也想省略“第三篇文章”(仅限活跃作者),你可以试试这个:
SELECT
p.id post_id, p.title post_title,
c.id as comment_id, c.title comment, c.is_active active_comment,
a.id author_id, a.title author, a.is_active active_author
FROM Post p
LEFT JOIN Comment c ON c.post_id = p.id
LEFT JOIN Author a ON a.id = c.author_id
WHERE c.is_active = 1 AND a.is_active = 1
ORDER BY p.id;
【讨论】:
【参考方案3】:这也应该这样做:
select a.title, p.title, c.title
from author a
left join comment c on a.id = c.author_id
left join post p on c.post_id = p.id
where a.id in (select id from author where is_active = 1)
and c.is_active = 1
http://sqlfiddle.com/#!2/e319e/1
【讨论】:
以上是关于MySQL 三表链式左连接,按最后一个表过滤的主要内容,如果未能解决你的问题,请参考以下文章