SQL выбор строк, где значение одного столбца является общим для другого столбца критериев
у меня есть таблица перекрестных ссылок, которая выглядит так:
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 ответов
Я предполагаю, что natrual ключ этой таблицы document_id + subject_id, и что 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 удаляется, потому что он не участвует в группировке. Сделав еще один шаг, я добавлю воображаемую таблицу subjects_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)
очевидно, subjects_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))
или что-то еще.
использование 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);
Это очень интересный вопрос.
Я предполагаю, что вы хотели бы более обобщенный запрос, но это то, что я бы сделал в случае, когда у вас всегда одинаковое количество предметов (скажем, два):
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
конечно, вы можете добавить еще одно внутреннее соединение, чтобы добавить еще один идентификатор темы.. Но я признаю, это не очень хорошее решение.
select document_id from table1
where subject_id in (17, 76)
group by document_id
having count(distinct subject_id) = 2