我可以选择在MySQL中加入哪些行

时间:2016-05-16 21:41:18

标签: mysql join

假设我有两个表,peopleemailsemailsperson_idaddressis_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
------------------------------------------------

2 个答案:

答案 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

sqlfiddle