SQL выбирает строки, в которых значение одного столбца является общим для столбца другого критерия - PullRequest
13 голосов
/ 11 сентября 2009

У меня есть таблица перекрестных ссылок, которая выглядит следующим образом:

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

Соответствует документам по темам. Документы могут быть членами более чем одной темы. Я хочу вернуть строки из этой таблицы, где данный документ соответствует всем темам в данном наборе. Например, дан набор предметов:

(17,76)

Я хочу вернуть только строки для документов, которые соответствуют всем темам в этом наборе (по крайней мере) где-то в таблице перекрестных ссылок. Требуемый выходной набор с учетом вышеуказанного набора будет:

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

Обратите внимание, что последняя строка таблицы не возвращается, поскольку этот документ соответствует только одному из обязательных предметов.

Какой самый простой и эффективный способ запросить это в SQL?

Ответы [ 4 ]

28 голосов
/ 11 сентября 2009

Я предполагаю, что естественным ключом этой таблицы является document_id + subject_id, и этот идентификатор является суррогатом; IOW, document_id и subject_id уникальны. Таким образом, я просто собираюсь притвориться, что его не существует и что на естественный ключ наложено уникальное ограничение.

Давайте начнем с очевидного.

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

Это дает вам все, что вы хотите плюс вещи, которые вам не нужны. Так что все, что нам нужно сделать, это отфильтровать другие вещи. «Другой материал» - это группы строк, число которых не равно количеству нужных предметов.

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

Обратите внимание, что subject_id удален, поскольку он не участвует в группировке. Сделав еще один шаг вперед, я собираюсь добавить воображаемую таблицу под названием subject_i_want, которая содержит N строк нужных вам предметов.

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)

Очевидно, что subject_i_want можно заменить другим подзапросом, временной таблицей или чем-то еще. Но, как только у вас есть этот список document_id, вы можете использовать его в под-выборе большего запроса.

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

Или что угодно.

2 голосов
/ 09 июня 2015

Использование Oracle (или любой базы данных, которая допускает предложение with). Это позволяет определять значения subject_id ровно один раз.

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);
1 голос
/ 11 сентября 2009

Это очень интересный вопрос.

Я предполагаю, что вам нужен более обобщенный запрос, но это то, что я бы сделал в случае, когда у вас всегда одинаковое количество предметов (скажем, два):

 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            

Конечно, вы можете добавить еще одно ВНУТРЕННЕЕ СОЕДИНЕНИЕ, чтобы добавить еще один идентификатор субъекта. Но я признаю, что это не очень хорошее общее решение.

0 голосов
/ 11 сентября 2009
select document_id from table1
 where subject_id in (17, 76)
 group by document_id
having count(distinct subject_id) = 2
...