2009-09-10 24 views
12

gemeinsam habe ich eine Querverweistabelle, die wie folgt aussieht:SQL Auswählen von Zeilen in dem Wert einer Spalte ist über eine andere Kriterien Spalte

id document_id subject_id 
1 8   21 
2 5   17 
3 5   76 
4 7   88 
5 9   17 
6 9   76 
7 2   76 

Es passt Dokumente zu Themen. Dokumente können Mitglieder von mehr als einem Thema sein. Ich möchte Zeilen aus dieser Tabelle zurückgeben, wo ein gegebenes Dokument alle die Themen in einem gegebenen Satz entspricht. Um zum Beispiel die Reihe von Themen gegeben:

(17,76)

ich nur die Zeilen für Dokumente zurückgeben möchten, die in diesem Satz (zumindest) irgendwo in der Querverweistabelle alle Themen entsprechen. Die gewünschte Ausgangsmenge des obigen Satzes gegeben wäre:

id document_id subject_id 
2 5   17 
3 5   76 
5 9   17 
6 9   76 

bemerken, dass die letzte Zeile der Tabelle nicht nur, weil das Dokument zurückgegeben entspricht einer der erforderlichen Themen.

Was ist die einfachste und effizienteste Möglichkeit, dies in SQL abzufragen?

+0

Es wäre toll zu wissen, wie Sie die Parameter für die Abfrage bereitstellen. Ich sehe eine Antwort, obwohl vollkommen in Ordnung, es wird nur für genau 2 Werte im Parametersatz funktionieren. Wenn Sie die Anzahl der Parameter begrenzen können, z. B. auf 10, dann ist es eine Unterhaltung. Wenn die App flexibel sein soll, werden die Vorschläge anders aussehen. – Eugene

+0

Danke, die Eingabe ist im Grunde "wählen Sie eine beliebige Anzahl von Themen", so dass die Menge der Betreff-IDs so groß wie die Anzahl der Fächer (theoretisch) wachsen kann. – Maciek

Antwort

27

Ich gehe davon aus, dass die natrual Schlüssel dieser Tabelle ist document_id + subject_id, und dass id ist ein Surrogat; IOW, document_id und subject_id sind einzigartig. Als solches werde ich nur so tun, als ob es nicht existiert und dass eine einzigartige Einschränkung auf dem natürlichen Schlüssel liegt.

Beginnen wir mit dem Offensichtlichen.

SELECT document_id, subject_id 
    FROM document_subjects 
WHERE subject_id IN (17,76) 

, dass man alles bekommt man will Plus Sachen, die Sie nicht wollen. Alles, was wir tun müssen, ist, die anderen Sachen herauszufiltern. Die "anderen Sachen" sind Gruppen von Reihen mit einer Zählung, die nicht gleich der Zählung der gewünschten Gegenstände ist.

SELECT document_id 
    FROM document_subjects 
WHERE subject_id IN (17,76) 
GROUP BY document_id 
HAVING COUNT(*) = 2 

Beachten Sie, dass subject_id entfernt wird, da es nicht an der Gruppierung beteiligt ist. Wenn ich diesen einen Schritt weiter führe, füge ich eine imaginäre Tabelle mit dem Namen subjects_i_want hinzu, die N Zeilen mit den gewünschten Fächern enthält.

SELECT document_id 
    FROM document_subjects 
WHERE subject_id IN (SELECT subject_id FROM subjects_i_want) 
GROUP BY document_id 
HAVING COUNT(*) = (SELECT COUNT(*) FROM subjects_i_want) 

Offensichtlich konnte subjects_i_want für eine andere Unterabfrage, temporäre Tabelle ausgelagert werden, oder was auch immer. Sobald Sie jedoch diese Liste von document_id haben, können Sie sie in einem Subselect einer größeren Abfrage verwenden.

SELECT document_id, subject_id, ... 
    FROM document_subjects 
WHERE document_id IN(
     SELECT document_id 
      FROM document_subjects 
      WHERE subject_id IN (SELECT subject_id FROM subjects_i_want) 
      GROUP BY document_id 
     HAVING COUNT(*) = (SELECT COUNT(*) FROM subjects_i_want)) 

Oder was auch immer.

+0

Super, danke. – Maciek

+1

+1 sehr nett, Alex. Ich habe in letzter Zeit einige Variationen dieser Frage bemerkt, und dies ist die klarste allgemeine Lösung, die ich bisher gesehen habe. – Matt

+0

+1, sehr nce und half mir, es wäre besser, wenn die Zählung (*) durchgeführt würde Ich würde auf unterschiedliche Einträge, da es die Möglichkeit der doppelten Daten berücksichtigt würde auch berücksichtigt werden; vorzugsweise COUNT (DISTINCT subject_id) statt COUNT (*) –

1

Das ist eine sehr interessante Frage.

Ich gehe davon aus Sie eine allgemeinere Abfrage würde, aber das ist, was ich in dem Fall tun würde, wo man immer die gleiche Anzahl von Themen haben (sagen wir zwei):

SELECT T.id, T.document_id, T.subject_id 
    FROM table T 
     INNER JOIN table T1 ON T.document_id = T1.document_id AND T1.subject_ID = 17 
     INNER JOIN table T2 ON T.document_id = T2.document_id AND T2.subject_ID = 76    

Natürlich Sie könnten noch einen weiteren INNEREN JOIN hinzufügen, um eine weitere Subjekt-ID hinzuzufügen. Aber ich gebe zu, es ist keine sehr gute allgemeine Lösung.

+0

D'oh, ich suche in der Tat nach einer Lösung, die einer beliebigen Anzahl von Subjekten entsprechen könnte. – Maciek

0
select document_id from table1 
where subject_id in (17, 76) 
group by document_id 
having count(distinct subject_id) = 2 
2

Verwenden von Oracle (oder einer anderen Datenbank, die die Klausel with erlaubt). Dies ermöglicht die genaue Definition der subject_id Werte.

with t as (select distinct document_id from table1 where subject_id in (17,76)) 
select document_id from table1 where subject_id in (select subject_id from t) 
group by document_id 
having count(*) = (select count (*) from t); 
+0

Ich fand diese Antwort am hilfreichsten, da sie auch für PostgreSQL gilt. – ramhiser

Verwandte Themen