2009-09-10 13 views
12

Ho una tabella di riferimento trasversale che assomiglia a questo:SQL selezionando le righe in cui il valore di una colonna è comune in un'altra colonna criteri

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 

Si abbina documenti a soggetti. I documenti possono essere membri di più di un argomento. Voglio restituire righe da questa tabella in cui un dato documento corrisponde allo a tutti gli oggetti in un determinato set di. Ad esempio, dato l'insieme di soggetti:

(17,76)

voglio tornare solo le righe per i documenti che corrispondono a tutti i soggetti nel set (almeno) da qualche parte nella tabella di riferimento croce. L'insieme uscita desiderata dato l'insieme sopra sarebbe:

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

noti che l'ultima riga della tabella non è restituito perché tale documento corrisponde una sola delle materie richieste.

Qual è il modo più semplice ed efficace per eseguire una query in SQL?

+0

Sarebbe bello sapere come si forniscono i parametri alla query. Vedo una risposta, mentre perfettamente a posto, funzionerà solo con esattamente 2 valori nel set di parametri. Se puoi limitare il numero di parametri, per esempio 10 max, allora è una conversazione. Se hai bisogno che l'app sia flessibile, i suggerimenti saranno diversi. – Eugene

+0

Grazie, l'input è fondamentalmente "scegli un numero qualsiasi di soggetti" in modo che l'insieme degli ID di soggetto possa crescere tanto quanto il numero di soggetti (in teoria). – Maciek

risposta

27

Si considera che la chiave natrual di questa tabella è document_id + icLsoggetto, e che id è un surrogato; IOW, document_id e subject_id sono unici. In quanto tale, farò finta che non esista e che un vincolo unico sia sulla chiave naturale.

Iniziamo con l'ovvio.

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

che si ottiene tutto quello che volete più cose che non vuoi. Quindi tutto ciò che dobbiamo fare è filtrare le altre cose. Le "altre cose" sono gruppi di righe con un conteggio che non è uguale al conteggio dei soggetti desiderati.

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

Si noti che subject_id viene rimosso perché non partecipa al raggruppamento. Facendo questo un ulteriore passo avanti, aggiungerò una tabella immaginaria chiamata subject_i_want che contiene N righe di argomenti che desideri.

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) 

Ovviamente subjects_i_want potrebbero essere scambiati per un altro subquery, tabella temporanea, o qualsiasi altra cosa. Ma, una volta che hai questa lista di document_id, puoi usarla all'interno di una sottoselezione di una query più grande.

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

O qualsiasi altra cosa.

+0

Fantastico, grazie. – Maciek

+1

+1 molto bello, Alex. Di recente ho notato alcune variazioni di questa domanda, e questa è la soluzione generale più chiaramente presentata che ho visto finora. – Matt

+0

+1, molto nce e mi ha aiutato, sarebbe meglio se il conteggio (*) eseguito I avendo sarebbe su voci distinte, in quanto eliminerebbe la possibilità di considerare anche i dati duplicati; preferibilmente COUNT (DISTINCT subject_id) invece di COUNT (*) –

1

Questa è una domanda molto interessante.

Sto assumendo che si desidera una query più generalizzato, ma questo è quello che vorrei fare nel caso in cui si ha sempre lo stesso numero di soggetti (diciamo due):

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    

Naturalmente, potresti aggiungere un altro JOIN INNER per aggiungere un altro ID soggetto .. Ma ammetto che non è una buona soluzione generale.

+0

D'oh, sto davvero cercando una soluzione che possa corrispondere a un numero arbitrario di soggetti. – Maciek

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

Utilizzo di Oracle (o qualsiasi database che consente la clausola with). Ciò consente la definizione dei valori subject_id esattamente una volta.

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

ho trovato che questa risposta è la più utile in quanto si applica anche a PostgreSQL. – ramhiser

Problemi correlati