2016-05-16 64 views
0

假設我有兩個表,peopleemailsemailsperson_id,一個addressis_primary我可以選擇我在MySQL中加入的行嗎

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 | [email protected] | true | 
| 2 |  | 2 |   1 | [email protected] | false | 
| 3 |  | 3 |   2 | [email protected] | true | 
| 4 |  | 4 |   4 | [email protected] | false | 
------  ----------------------------------------- 

是有辦法得到這樣的結果:

------------------------------------------------ 
| people.id | emails.id | address | is_primary | 
------------------------------------------------ 
|   1 |   1 | [email protected] | true | 
|   2 |   3 | [email protected] | true | // chosen over [email protected] because it's primary 
|   3 |  null | null | null | // no email for person 3 
|   4 |   4 | [email protected] | false | // no primary email for person 4 
------------------------------------------------ 
+0

LEFT JOIN不爲 「只有一個」;無論是否存在任何匹配的「右側」,它都是從左側開始的每一行。如果右邊有多個,那麼匹配的左邊行仍然會有多個數據副本;如果右側沒有,則仍然有左側行數據的一個副本,但對於右側的任何字段都有NULL。 – Uueerdo

+0

如果身份證號碼= 4的人有第二封電子郵件也不是主要的 - 您想要顯示哪一個?另一種情況:如果一個人有兩個主要電子郵件會怎麼樣?那麼'emails'表的主鍵是什麼? –

+1

我總是選擇is_primary = true over is_primary = false,因爲我想通過電子郵件發送他們最好的電子郵件地址。電子郵件的主鍵是emails.id,我只是懶得輸入所有列。我會添加它。如果一個人有兩個主要電子郵件,那麼其中任何一個(第一個通過id都可以),如果他們有多個非主要ID,那麼第一個也是好的。 – whiterook6

回答

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) 

編輯:

保羅·施皮格爾的回答是不錯的,所以我給予好評,但我不知道這是否表現良好,因爲它有一個依賴子查詢。所以我創建了這個查詢。它可能取決於你的數據。試試兩個答案。

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 
+0

我會編輯我的問題。基本上,我想要一個人的主要電子郵件地址,如果有的話,或其他人的任何其他電子郵件地址。如果有幫助的話,我找不出更好的措詞「prefer is_primary = true」。 – whiterook6

+0

我收到一個錯誤:「未知列'e2.address'」。你需要刪除別名'作爲e_id' –

+0

哎呀,謝謝。 – fancyPants

1

的LEFT的ON子句中使用相關子查詢與LIMIT 1 JOIN:

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

+0

這是一個很好的答案,雖然我不確定表現。如果你有興趣,我提供了一個替代品,這可能比你的表現更差。可能取決於OP的數據。無論如何,+1 – fancyPants

+0

@fancyPants - 電子郵件索引(person_id,is_primary)將支持子查詢。 MySQL應該緩存每個person_id的子查詢結果。但是,根據數據分佈和索引,多次嵌套sunqueries與最小/最大搜索可能會更快。 –

相關問題