Можно ли упростить этот SQL-запрос? - PullRequest
2 голосов
/ 11 мая 2010

У меня есть следующие таблицы:
Person, {"Id", "Name", "LastName"}
Спорт, {"Id", "Имя", "Тип"}
SportsPerPerson, {"Id", "PersonId", "SportsId"}

Для моего запроса я хочу получить всех лиц, которые занимаются определенным видом спорта, в то время как в моем распоряжении есть только атрибут "Имя". Чтобы получить правильные строки, я вычислил следующие запросы:

SELECT *
FROM Person
WHERE Person.Id in 
(
    SELECT SportsPerPerson.PersonId FROM SportsPerPerson
    INNER JOIN Sports on SportsPerPerson.SportsId = Sports.Id
    WHERE Sports.Name = 'Tennis'
)
AND Person.Id in 
(
    SELECT SportsPerPerson.PersonId FROM SportsPerPerson
    INNER JOIN Sports on SportsPerPerson.SportsId = Sports.Id
    WHERE Sports.Name = 'Soccer'
)

OR

SELECT *
FROM Person
WHERE Id IN
    (SELECT PersonId FROM SportsPerPerson WHERE SportsId IN 
        (SELECT Id FROM Sports WHERE Name = 'Tennis'))
AND Id IN
    (SELECT PersonId FROM SportsPerPerson WHERE SportsId IN 
        (SELECT Id FROM Sports WHERE Name = 'Soccer'))

Теперь мой вопрос: нет ли более простого способа написать этот запрос? Использование просто ИЛИ не сработает, потому что мне нужен человек, который играет в теннис и футбол. Но использование AND также не работает, потому что значения не находятся в одной строке.

Ответы [ 5 ]

2 голосов
/ 11 мая 2010

Вы можете использовать другой JOIN, чтобы избежать второго IN. Суб-выбор возвращает только тех, кто играет в теннис и футбол:

SELECT *
FROM Person
WHERE Person.Id IN
(
    SELECT spp1.PersonId
    FROM SportsPerPerson spp1
    JOIN SportsPerPerson spp2 ON ( spp2.PersonId = spp1.PersonId )
    JOIN Sports s1 on spp1.SportsId = s1.Id
    JOIN Sports s2 on spp2.SportsId = s2.Id
    WHERE s1.Name = 'Tennis'
      AND s2.Name = 'Soccer'
)
1 голос
/ 11 мая 2010

В запросе следует использовать два объединения:

SELECT *
FROM Person p INNER JOIN SportsPerPerson spp1 ON (p.PersonId = spp1.PersonId)
              INNER JOIN Sports s1 ON (s1.SportsIN = spp1.SportId)
              INNER JOIN SportsPerPerson spp2 ON (p.PersonId = spp2.PersonId)
              INNER JOIN Sports s2 ON (s2.SportId = spp2.SportId)
    WHERE s1.Name = 'Tennis' AND s2.Name='Soccer'
0 голосов
/ 11 мая 2010

Вам нужен запрос:

SELECT p.ID, p.Name, p.LastName
FROM Person p
JOIN SportsPerPerson sp ON p.ID = sp.PersonID 
JOIN Sports s ON sp.SportsID = s.ID
WHERE s.Name = 'Football'

Тем не менее, помимо этого, ключ ID в таблице SportsPerPerson совершенно не нужен для реализации ваших отношений «многие ко многим». Было бы достаточно использовать столбцы PersonID и SportID в качестве составного первичного ключа.

0 голосов
/ 11 мая 2010

Это:

SELECT  *
FROM    Person p
WHERE   (
        SELECT  COUNT(*)
        FROM    Sports s
        JOIN    SportsPerPerson sp
        ON      sp.SportsID = s.id
        WHERE   s.name IN ('Tennis', 'Soccer')
                AND sp.PersonID = p.id
        ) = 2

или это:

SELECT  p.*
FROM    (
        SELECT  sp.PersonID
        FROM    Sports s
        JOIN    SportsPerPerson sp
        ON      sp.SportsID = s.id
        WHERE   s.name IN ('Tennis', 'Soccer')
        GROUP BY
                sp.PersonID
        HAVING  COUNT(*) = 2
        ) q
JOIN    person p
ON      p.id = q.personID

Вам нужно объявить UNIQUE KEY или PRIMARY KEY на SportsPerPerson (sportsid, personid) для правильной и быстрой работы.

0 голосов
/ 11 мая 2010

Хитрость в том, чтобы использовать псевдонимы, чтобы вы могли использовать одни и те же таблицы несколько раз:

SELECT p.*
FROM Person p
INNER JOIN SportsPerPerson spa
  ON p.Id = spa.PersonId
INNER JOIN Sports sa
  ON spa.SportsId = sa.Id
INNER JOIN SportsPerPerson spb
  ON p.Id = spb.PersonId
INNER JOIN Sports sb
  ON spb.SportsId = sb.Id
WHERE
  sa.Name = 'Tennis'
  AND sb.Name = 'Soccer'
...