SQL che seleziona le righe in cui il valore di una colonna è comune in un'altra colonna di criteri

StackOverflow https://stackoverflow.com/questions/1408141

  •  05-07-2019
  •  | 
  •  

Domanda

Ho una tabella di riferimenti incrociati che assomiglia a questa:

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

Abbina i documenti ai soggetti. I documenti possono essere membri di più di un argomento. Voglio restituire righe da questa tabella in cui un determinato documento corrisponde a tutti gli argomenti di un determinato set. Ad esempio, dato l'insieme dei soggetti:

(17,76)

Voglio restituire solo le righe per i documenti che corrispondono a tutti gli argomenti di quell'insieme (almeno) da qualche parte nella tabella dei riferimenti incrociati. Il set di output desiderato dato il set sopra sarebbe:

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

Si noti che l'ultima riga della tabella non viene restituita perché quel documento corrisponde solo a uno degli argomenti richiesti.

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

È stato utile?

Soluzione

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

Cominciamo con l'ovvio.

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

Questo ti dà tutto ciò che vuoi 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 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

Nota che subject_id viene rimosso perché non partecipa al raggruppamento. Facendo un ulteriore passo avanti, aggiungerò una tabella immaginaria chiamata subject_i_want che contiene N file 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 subject_i_want potrebbe essere sostituito con un'altra subquery, tabella temporanea o altro. Ma, una volta che hai questo elenco di document_id, puoi usarlo 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 qualunque altra cosa.

Altri suggerimenti

Uso di Oracle (o di qualsiasi database che consenta 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);

Questa è una domanda molto interessante.

Suppongo che vorresti una domanda più generalizzata, ma questo è quello che farei nel caso in cui tu abbia sempre lo stesso numero di argomenti (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            

Ovviamente, potresti aggiungere un altro INNER JOIN per aggiungere un altro ID soggetto. Ma ammetto che non è un'ottima soluzione generale.

select document_id from table1
 where subject_id in (17, 76)
 group by document_id
having count(distinct subject_id) = 2
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top