PostgreSQL: отношение «один ко многим» с несколькими условиями на стороне многих

#sql #postgresql #join #relational-division #in-subquery

#sql #PostgreSQL #Присоединиться #реляционное разделение #Подзапрос

Вопрос:

У меня есть таблица Students, в которой есть следующие столбцы

 id
student_name
 

И таблица курсов

 id
student_id  //a foreign key
course_name
credits
 

Это отношение «один ко многим» (у одного студента может быть несколько курсов).

Я хочу перечислить всех студентов, у которых есть эти 2 курса

 first course:   course_name -> math    , credit -> 5
second course:  course_name -> history , credit -> 3
 

Пожалуйста, обратите внимание, что каждый студент должен пройти по крайней мере эти два курса.

Я могу получить то, что хочу, присоединившись к таблице Courses дважды (один раз для первого курса и еще один для второго курса), но в случае, если я захочу добавить еще один курс к условию, мне нужно будет присоединиться еще раз.

Так что, пожалуйста, не могли бы вы направить меня к другому подходу.

Комментарии:

1. Вы хотите, чтобы у каждого учащегося были именно эти два курса или, по крайней мере , эти два курса (но может быть больше)

2. Не связано с вашим вопросом, но разве это не должно быть отношением «многие ко многим»? Один курс может содержать несколько студентов, и один студент записывается на несколько курсов

3. По крайней мере, эти 2 курса

4. Правда, лучше быть «многие ко многим», но я просто использовал этот пример, чтобы упростить вопрос

Ответ №1:

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

демонстрация: db<>скрипка

 SELECT
    s.id
FROM students s
JOIN courses c
ON c.student_id = s.id
GROUP BY s.id
HAVING SUM(
    ((c.course_name = 'math' AND c.credits = 5)
    OR
    (c.course_name = 'history' AND c.credits = 3))::int
) = 2
 

В качестве альтернативы SUM(condition::int) вы могли бы использовать COUNT() FILTER предложение with a:

демонстрация: db<>скрипка

 HAVING COUNT(*) FILTER (WHERE
    (c.course_name = 'math' AND c.credits = 5)
    OR
    (c.course_name = 'history' AND c.credits = 3)
) = 2
 

Комментарии:

1. спасибо за усилия, которые вы приложили к ответу, в демо-версии это выглядит нормально, но чтобы заставить его работать в моем случае, я пытаюсь понять, как сумма равна 2, когда вы используете ИЛИ, поэтому я попробовал это select (true or true)::int as x , и это дает мне 1, как я и ожидал. Не могли бы вы объяснить, как вы получаете сумму 2?

2. Вы правы: условие bool дает 0 или 1. Поскольку student _id == 2 (в моей скрипке) соответствует этому условию дважды (первое для math , второе для history записи), сумма двух совпадений равна 2. Итак, если вам нужно добавить третье условие, сумма должна быть равна 3, потому что таблица должна соответствовать условию три раза. Я добавил промежуточный шаг здесь: dbfiddle.uk /…

3. Спасибо за объяснение и элегантное решение