2012-06-15 13 views
8

Ich habe zwei Tabellen in meiner MySQL-Datenbank, eine ist eine Bibliothek aller Bücher in der Datenbank, und die andere enthält einzelne Zeilen, die Bücher entsprechen in einer Benutzerbibliothek.MySQL Left Outer Join, Elemente in zweiter Tabelle ausschließen Benutzer

Zum Beispiel:

Bibliothek Tabelle

`id`  `title`... 
=====  =========== 
    1   Moby Dick 
    2   Harry Potter 

Sammlung Tabelle

`id`  `user`  `book` 
=====  ======  ======= 
    1   1   2 
    2   2   2 
    3   1   1 

Was ich tun möchte, wird eine Abfrage ausführen, die alle Bücher zeigen, die nicht in einer Benutzersammlung. Ich kann diese Abfrage ausführen, alle Bücher zu zeigen, nicht in jedes Benutzer-Sammlung:

SELECT * 
FROM `library` 
LEFT OUTER JOIN `collection` ON `library`.`id` = `collection`.`book` 
WHERE `collection`.`book` IS NULL 

Dieses so weit ganz gut funktioniert, wie ich sagen kann. Wenn Sie dies in PHPMyAdmin ausführen, werden alle Bücher ausgegeben, die nicht in der Sammeltabelle enthalten sind.

Wie kann ich das auf einen bestimmten Benutzer beschränken? Mit den obigen Dummy-Daten möchte ich zum Beispiel Buch 1 erhalten, wenn Benutzer 2 die Abfrage ausführt, und keine Bücher, wenn Benutzer 1 die Abfrage ausführt.

Nur eine AND user=[id] hinzufügen funktioniert nicht, und mit meinem extrem begrenzten Wissen von JOIN Aussagen komme ich nirgendwohin wirklich.

Auch die ID der Ergebnisse zurückgegeben (der Abfrage angezeigt, die nicht das tut, was ich will, aber funktioniert) ist 0-- Wie stelle ich sicher, dass die zurückgegebene ID ist die von library.id?

+0

Bitte zeigen Sie die tatsächliche Tabellenstruktur und geben Sie uns den Fehler (falls vorhanden) Sie begegnen. Ihre Abfrage erwähnt ein Feld namens "Artikel", das nicht in Ihren Tabellenbeschreibungen enthalten ist. – bpeterson76

+0

Ups-- Ich habe es als Artikel verlassen, als ich kopiert und eingefügt habe. Es sollte "Buch" sein. Ich habe die Frage geändert. –

Antwort

12

Sie müssen verengen Ihre LEFT JOIN Auswahl nur auf die Bücher, die ein bestimmter Benutzer hat, dann was NULL in der verknüpften Tabelle ist wird Zeilen (Bücher), für die der Benutzer nicht in seinem/hat ihre Kollektion:

SELECT 
    a.id, 
    a.title 
FROM 
    library a 
LEFT JOIN 
    (
     SELECT book 
     FROM collection 
     WHERE user = <userid> 
    ) b ON a.id = b.book 
WHERE 
    b.book IS NULL 

Eine Alternative ist:

SELECT 
    a.id, 
    a.title 
FROM 
    library a 
WHERE 
    a.id NOT IN 
    (
     SELECT book 
     FROM collection 
     WHERE user = <userid> 
    ) 

jedoch die erste Lösung optimalere wie MySQL ist die NOT IN Unterabfrage einmal für jede ro ausführen w statt nur einmal für die gesamte Abfrage. Intuitiv würden Sie erwarten, dass MySQL die Unterabfrage einmal ausführt und als Liste verwendet, aber MySQL ist nicht intelligent genug, um zwischen korrelierten und nicht korrelierten Unterabfragen zu unterscheiden.

Wie bereits erwähnt here: „Das Problem ist, dass für eine Anweisung, die eine IN Unterabfrage verwendet, die Optimierer es als eine korrelierte Unterabfrage umschreibt“

+0

Danke! Das hat perfekt funktioniert. Dieses 'JOIN'-Zeug, aus welchem ​​Grund auch immer, ist schwer für mich, um meinen Kopf zu wickeln ... –

+2

Ihr letzter Absatz zeigt auf einen Link, wo' IN' Unterabfragen erwähnt werden und nicht 'NOT IN' Unterabfragen. Also, was Sie implizieren, dass Abfragen mit 'NOT IN' nicht gut optimiert sind, ist falsch. Sehen Sie sich diesen großartigen Blogpost an: [NOT IN vs. NOT EXISTS vs. LINKER JOIN/IS NULL: MySQL] (http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs- left-join-is-null-mysql /) –

+0

Ich glaube, es ist egal, ob es 'NOT IN' oder' IN' ist. Das "NOT" invertiert einfach den von "IN" zurückgegebenen Booleschen Wert. Interessanter Artikel obwohl +1 –

3

Wie wäre es damit? Es ist nur von Anfang an - ich habe keinen Zugang zu einer Datenbank, an der ich gerade testen kann.(Entschuldigung)

SELECT 
    * 
FROM 
    library lib 
WHERE 
    lib.id NOT IN (
     SELECT 
     book 
     FROM 
     collection coll 
     WHERE 
     coll.user =[id] 
    ) 
; 
+0

Ah ... Ich sehe Andrew M schlug mich dazu: O) –

+0

Danke für Ihre Hilfe sowieso: P +1 –

+1

Danke für die +1. Ich brauche diese dringend, da ich hier ganz neu bin: O) –