2016-05-16 9 views
0

Angenommen, ich habe zwei Tabellen, people und emails. emails hat eine person_id, ein address und ein is_primary:Kann ich selektiv sein, auf welchen Zeilen ich mich in MySQL anmelde

people: 
    id 

emails: 
    person_id 
    address 
    is_primary 

Um alle E-Mail-Adressen pro Person zu bekommen, was ich tun kann eine einfache beitreten:

select * from people join emails on people.id = emails.person_id 

Was passiert, wenn ich nur (höchstens) wollen eine Zeile von der rechten Tabelle für jede Zeile in der linken Tabelle? Und wenn eine bestimmte Person mehrere E-Mails hat und eine als is_primary markiert ist, gibt es eine Möglichkeit, die zu verwendende Zeile beim Verbinden zu bevorzugen?

Also, wenn ich

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

haben, ist es eine Möglichkeit, um dieses Ergebnis zu erhalten:

------------------------------------------------ 
| 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 nicht "nur ein" ist; Es ist jede Zeile von links, unabhängig davon, ob es "rechts" gibt. Wenn es auf der rechten Seite mehrere gibt, werden immer noch mehrere Kopien der Daten aus der übereinstimmenden linken Zeile sein; Wenn rechts keine vorhanden sind, haben Sie immer noch eine Kopie der Daten der linken Zeile, aber NULL für alle Felder von rechts. – Uueerdo

+0

Wenn Person mit ID = 4 eine zweite E-Mail hat, die auch nicht primär ist - welche möchten Sie zeigen? Ein weiterer Fall: Was passiert, wenn eine Person zwei primäre E-Mails hat? Und was ist der Hauptschlüssel der 'emails' Tabelle? –

+1

Ich würde immer is_primary = true über is_primary = false wählen, da ich ihre besten E-Mail-Adressen senden möchte. Der Primärschlüssel von E-Mails ist email.id, ich war einfach zu faul, alle Spalten einzugeben. Ich werde es hinzufügen. Wenn eine Person zwei primäre E-Mails hat, dann eine der beiden (die erste nach ID ist in Ordnung) und wenn sie mehrere nicht-primäre IDs haben, dann ist die erste auch in Ordnung. – whiterook6

Antwort

2

Du hast es ein bisschen falsch, wie links/rechts Arbeit verbindet. Diese

beitreten

select * from people join emails on people.id = emails.person_id 

können Sie jede Spalte aus beiden Tabellen für alle Datensätze, die Ihren ON Zustand entsprechen.

Die LEFT JOIN

select * from people left join emails on people.id = emails.person_id 

Sie von Menschen jeden Datensatz geben, unabhängig davon, ob es in E-Mails oder kein entsprechender Datensatz ist. Wenn nicht, werden die Spalten aus der E-Mail-Tabelle nur NULL sein.

Wenn eine Person mehrere E-Mails hat, werden mehrere Datensätze im Ergebnis für diese Person angezeigt. Anfänger wundern sich dann oft, warum die Daten doppelt vorhanden sind.

Wenn Sie die Daten zu den Zeilen beschränken wollen, wo is_primary den Wert 1, können Sie dies in der WHERE Klausel tun, wenn Sie tun eine innere Verknüpfung (Ihre erste Abfrage, obwohl Sie die inner Schlüsselwort weggelassen).

Wenn Sie eine Links-/Rechts-Join-Abfrage haben, müssen Sie diesen Filter in die ON-Klausel einfügen. Wenn Sie es in die WHERE-Klausel einfügen würden, würden Sie den Links/Rechts-Join implizit in einen inneren Join verwandeln, weil die WHERE-Klausel die NULL Zeilen filtern würde, die ich oben erwähnte. Oder Sie könnten die Abfrage so schreiben:

select * from people left join emails on people.id = emails.person_id 
where (emails.is_primary = 1 or emails.is_primary is null) 

EDIT nach Klärung:

Paul Spiegel Antwort ist gut, deshalb meine upvote, aber ich bin nicht sicher, ob es gut funktioniert, da es hat eine abhängige Unterabfrage. Also habe ich diese Abfrage erstellt. Es kann jedoch von Ihren Daten abhängen. Probiere beide Antworten aus.

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

Ich werde meine Frage bearbeiten. Im Wesentlichen möchte ich die primäre E-Mail-Adresse einer Person, wenn es eine andere oder eine andere E-Mail-Adresse für die Person gibt. Ich kann mir keine bessere Formulierung ausdenken, die "lieber is_primary = true" ist, wenn das hilft. – whiterook6

+0

Ich erhalte eine Fehlermeldung: "Unbekannte Spalte 'e2.address'". Sie müssen den Alias ​​'as e_id' entfernen. –

+0

Hoppla, danke. – fancyPants

1

eine korrelierte Unterabfrage mit LIMIT 1 in der ON-Klausel der LEFT 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

Es ist eine gute Antwort, obwohl ich mir über die Leistung nicht sicher bin. Falls Sie interessiert sind, habe ich eine Alternative zur Verfügung gestellt, die genauso gut funktionieren könnte wie Ihre. Könnte von OP-Daten abhängen. Wie auch immer, +1 – fancyPants

+0

@fancyPants - ein Index für E-Mails (person_id, is_primary) würde die Unterabfrage unterstützen. Und MySQL sollte das Unterabfrageergebnis für jede person_id zwischenspeichern. Je nach Datenverteilung und Indizes sind jedoch mehrfach verschachtelte Sunqueries mit Min/Max-Suche möglicherweise schneller. –

Verwandte Themen