выбор на основе всех условий (реляционное деление) - PullRequest
1 голос
/ 20 февраля 2012

У меня есть таблицы question, topic и question_has_topic (отношение «многие ко многим»). В моем приложении администраторы видят разбивку по вопросам, сгруппированным по темам, и выбирают, сколько из них они хотели бы, чтобы система выбрала случайным образом для создания теста.
Вот такую ​​таблицу они видят:

+-----------------------+---------------------+------------+
|        Topics         | Questions available | Selection: | 
+-----------------------+---------------------+------------+
| health,safety,general |                  13 |            |
| health                |                   3 |            |
| safety                |                   7 |            |
| general               |                   1 |            |
+-----------------------+---------------------+------------+

Количество уникально для определенной группы тем. В любом случае, после того, как они сделают выбор, мне понадобится оператор SQL, который выберет вопросы, которые соответствуют данной группе тем. То есть Мне может понадобиться 3 вопроса, в которых есть темы: здоровье, безопасность и общие вопросы.
Я проводил некоторые онлайн-исследования и думаю, что то, что я пытаюсь сделать, называется разделением в реляционной алгебре, и вот моя попытка произвольная группировка топиков:

select questionid from question_has_topic
where not exists (
    select questionid from question_has_topic
    where topicid not in (8,9,10))

Результат пуст, хотя есть 2 вопроса в базе данных, которые имеют все эти идентификаторы тем, которые говорят мне, что это не работает. Я следовал примеру из этой ссылки

Ответы [ 2 ]

2 голосов
/ 20 февраля 2012

РЕДАКТИРОВАТЬ : удалил мой старый пост, так как я неправильно прочитал вопрос.


Это техника, которую я использовал в прошлом:

  SELECT qht.questionid
    FROM question_has_topic AS qht
   WHERE qht.topicid IN (8,9,10)
GROUP BY qht.questionid
  HAVING COUNT(*) = 3 AND
         COUNT(*) = (SELECT COUNT(*) FROM question_has_topic AS dupe
                     WHERE dupe.questionid = qht.questionid)

, где 3 соответствует количеству тем в данной группе. Это предполагает, что каждая пара (questionid, topicid) в question_has_topic уникальна (что должно быть в таблице отношений «многие ко многим»).

Способ работы этого запроса заключается в том, чтобы сначала выбрать любой вопрос, которому назначена хотя бы одна из заданных тем (WHERE qht.topicid IN (8,9,10)), а затем сгруппировать по questionid. Первое предложение HAVING (COUNT(*) = 3) может быть истинным, только если для данного вопроса назначены все три темы (поскольку мы предполагаем, что дубликаты в этой таблице не допускаются). Второе предложение HAVING проверяет общее количество тем, которые назначены на вопрос. Это необходимо для защиты от случая, когда, например, для вопроса могут быть назначены темы 8, 9, 10 и 11.

2 голосов
/ 20 февраля 2012

Я думаю, это то, что вы пытались написать, но это очень неэффективный способ сделать это -

SELECT questionid FROM question WHERE NOT EXISTS (
    SELECT topicid FROM topic WHERE topicid NOT IN (
        SELECT topicid FROM question_has_topic WHERE question.questionid = question_has_topic.questionid
    ) AND topicid IN (8, 9, 10)
);

Это определенно намного быстрее -

SELECT *
FROM question_has_topic t1
INNER JOIN question_has_topic t2
    ON t1.questionid = t2.questionid AND t2.topicid = 9
INNER JOIN question_has_topic t3
    ON t2.questionid = t3.questionid AND t3.topicid = 10
WHERE t1.topicid = 8;

ОБНОВЛЕНИЕ: я знал, что был более простой ответ. Метод Черана намного проще и должен работать немного быстрее, чем ВНУТРЕННИЕ СОЕДИНЕНИЯ. Пожалуйста, примите его ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...