假设我有两个表,people
和emails
。 emails
有person_id
,address
和is_primary
:
people:
id
emails:
person_id
address
is_primary
要获得每个人的所有电子邮件地址,我可以进行简单的加入:
select * from people join emails on people.id = emails.person_id
如果我只希望(最多)左表中每行的右表中的一行怎么办?而且,如果某个人有多封电子邮件而且其中一封被标记为is_primary
,是否有办法在加入时更喜欢使用哪一行?
所以,如果我有
people: emails:
------ -----------------------------------------
| id | | id | person_id | address | is_primary |
------ -----------------------------------------
| 1 | | 1 | 1 | a@b.c | true |
| 2 | | 2 | 1 | b@b.c | false |
| 3 | | 3 | 2 | c@b.c | true |
| 4 | | 4 | 4 | d@b.c | false |
------ -----------------------------------------
有没有办法得到这个结果:
------------------------------------------------
| people.id | emails.id | address | is_primary |
------------------------------------------------
| 1 | 1 | a@b.c | true |
| 2 | 3 | c@b.c | true | // chosen over b@b.c because it's primary
| 3 | null | null | null | // no email for person 3
| 4 | 4 | d@b.c | false | // no primary email for person 4
------------------------------------------------
答案 0 :(得分:2)
你有点不对劲,左/右连接如何工作。
此联接
select * from people join emails on people.id = emails.person_id
对于符合ON
条件的所有记录,将从两个表中获取每一列。
左连接
select * from people left join emails on people.id = emails.person_id
无论电子邮件中是否有相应的记录,都会向您提供人们的每条记录。如果没有,则电子邮件表中的列将只是NULL
。
如果某人有多封电子邮件,则此人的结果中会有多条记录。初学者经常想知道为什么数据重复。
如果要将数据限制为is_primary
的值为1
的行,则可以在WHERE
子句中进行内连接时执行此操作(您的第一个查询,但您省略了inner
关键字)。
如果有左/右连接查询,则必须将此过滤器放在ON
子句中。如果你将它放在WHERE
子句中,你会隐式地将左/右连接转换为内连接,因为WHERE
子句将过滤我上面提到的NULL
行。或者您可以像这样编写查询:
select * from people left join emails on people.id = emails.person_id
where (emails.is_primary = 1 or emails.is_primary is null)
澄清后编辑:
Paul Spiegel的答案很好,因此我的支持,但我不确定它是否表现良好,因为它有一个从属子查询。所以我创建了这个查询。这可能取决于您的数据。试试这两个答案。
select
p.*,
coalesce(e1.address, e2.address) AS address
from people p
left join emails e1 on p.id = e1.person_id and e1.is_primary = 1
left join (
select person_id, address
from emails e
where id = (select min(id) from emails where emails.is_primary = 0 and emails.person_id = e.person_id)
) e2 on p.id = e2.person_id
答案 1 :(得分:1)
在LEFT JOIN的ON子句中使用带有LIMIT 1
的相关子查询:
select *
from people p
left join emails e
on e.person_id = p.id
and e.id = (
select e1.id
from emails e1
where e1.person_id = e.person_id
order by e1.is_primary desc, -- true first
e1.id -- If e1.is_primary is ambiguous
limit 1
)
order by p.id