views:

87

answers:

5

I have the following tables:
Person, {"Id", "Name", "LastName"}
Sports, {"Id" "Name", "Type"}
SportsPerPerson, {"Id", "PersonId", "SportsId"}

For my query I want to get all the Persons that excersise a specific Sport whereas I only have the Sports "Name" attribute at my disposal. To retrieve the correct rows I've figured out the following queries:

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'))

Now my question is, isn't there an easier way to write this query? Using just OR won't work because I need the person who play 'Tennis' AND 'Soccer'. But using AND also doesn't work because the values aren't on the same row.

+1  A: 

You should use two joins in the query:

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'
PanJanek
This one does the trick :]
Bas
+2  A: 

You can use another JOIN to avoid the second IN. The sub-select only returns those persons that play both Tennis and Soccer:

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'
)
Peter Lang
@Peter Lang: Congrats on 10K. :)
Mark Byers
@Mark: Thanks! Guess we are going to congratulate you on 50K soon? :)
Peter Lang
Strangely enough the query returns 0 records :o I like the idea though but I just tested it and it doesn't seem to work. It returns 0 records, where my own queries return 55 >.<
Bas
@Bas: Sorry, I was missing another join. Please try my updated answer.
Peter Lang
Cheers, it works :]
Bas
A: 

The trick is to use aliases so that you can use the same tables multiple times:

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'
Ignacio Vazquez-Abrams
A: 

This:

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

or this:

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

You need to declare a UNIQUE KEY or a PRIMARY KEY on SportsPerPerson (sportsid, personid) for this to work correctly and fast.

Quassnoi
A: 

The query you need is:

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'

That said, as an aside, the ID key on the SportsPerPerson table is entirely unnecessary to implement the many to many relationship you have. Using the PersonID and SportID columns as a composite primary key would be enough.

Ira Rainey
This doesn't really help me... If I add another sports, which is my intention, it returns me 0 records because the sports aren't in the same row. Also OR won't help me because then it will also return the persons which have EITHER 'Soccer' or 'Tennis' and I need both
Bas