Запись, связанная со всеми записями другой таблицы в SQL - PullRequest
1 голос
/ 19 июля 2010

Допустим, у нас есть три таблицы в реляционной базе данных:

Person {id, name}
Obstacle {id, name}
Person_overcomes_obstacle {person_id, obstacle_id}

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

Ответы [ 4 ]

4 голосов
/ 19 июля 2010

Я заметил, что я был единственным человеком, который использовал псевдоним таблицы для Person_overcomes_obstacle!

Для этого вам нужен запрос реляционного деления .

Вы можете подсчитать препятствия и соответствующие записи в Person_overcomes_obstacle и вернуть те, у которых совпадают 2 числа, или посмотреть на это как на поиск людей, для которых нет препятствий, которые они не преодолели.

SELECT p.id, p.name /*Or use COUNT(*) or wrap in Exists 
                     if you don't care about ids and names*/
FROM Person p
WHERE NOT EXISTS
   (SELECT * FROM Obstacle o
    WHERE NOT EXISTS 
    (
       SELECT * FROM Person_overcomes_obstacle poo
       WHERE poo.person_id = p.id and o.id = poo.obstacle_id
    )
)
3 голосов
/ 19 июля 2010
SELECT 
    p.name,
    COUNT(DISTINCT oo.obstacle_id) AS OBSTACLES_COMPLETED
FROM 
    person p

    JOIN person_overcomes_obstacle oo
    ON oo.person_id = p.person_id

GROUP BY 
    p.name

HAVING 
    COUNT(DISTINCT poo.obstacle_id) = (SELECT COUNT(id) FROM obstacle)
1 голос
/ 19 июля 2010

Использование:

SELECT poo.person_id
    FROM PERSON_OVERCOMES_OBSTACLE poo
GROUP BY poo.person_id
    HAVING COUNT(DISTINCT poo.obstacle_id) = (SELECT COUNT(*) 
                                                                        FROM OBSTACLE)

Это альтернатива, которая менее эффективна:

SELECT x.person_id
   FROM (SELECT poo.person_id,
                         COUNT(DISTINCT poo.obstacle_id) AS obs_overcome,
                         (SELECT COUNT(*) 
                             FROM OBSTACLE) AS obs_total
                 FROM PERSON_OVERCOMES_OBSTALCE poo
          GROUP BY poo.person_id) x
 WHERE x.obs_overcome = x.obs_total

В любом случае вы можете присоединиться к таблице PERSON, чтобы получить больше информации, если хотите, или вы можете запустить подсчет person_id из любого запроса, чтобы узнать, сколько людей преодолело все записанные препятствия.

1 голос
/ 19 июля 2010

Этот SELECT должен возвращать количество препятствий, которые никто не преодолел.Если счет равен нулю, то все препятствия были преодолены хотя бы одним человеком.

SELECT count(*)
  FROM (SELECT po.person_id, o.obstacle_id
          FROM Obstacle o
          LEFT OUTER JOIN Person_overcomes_obstacle po
            ON (o.obstacle_id = po.obstacle_id)) t
WHERE t.person_id IS NULL

Вы также можете сделать это для того же эффекта (и, возможно, лучшей производительности):

SELECT count(*)
  FROM Obstacle o
 WHERE NOT EXISTS (SELECT 1
                     FROM Person_overcomes_obstacle po
                    WHERE po.obstacle_id = o.obstacle_id)

РЕДАКТИРОВАТЬ: Как указано в комментариях, два вышеупомянутых запроса только доказывают, что нет никаких препятствий, которые никто не преодолел, и что ни один человек не преодолел все препятствия.все еще можно доказать, что один пользователь преодолел все препятствия без запроса таблицы Person:

SELECT t.personid, count(*)
  FROM (SELECT DISTINCT po.person_id, o.obstacle_id
          FROM Obstacle o
          JOIN Person_overcomes_obstacle po
            ON (o.obstacle_id = po.obstacle_id)) t
 GROUP BY t.persion_id
 HAVING count(*) = (SELECT count(*)
                      FROM obstacle)
...