简体   繁体   中英

SQL query records containing all items in list

I have the following tables:

MOVIES

MOVIE_ID TITLE
---------- -----------------------------
         1 The Shawshank Redemption
         2 The Godfather
         3 The Godfather: Part II
         4 The Dark Knight
         5 Pulp Fiction
         6 The Good, the Bad and the Ugly
         7 Schindler's List
         8 Angry Men
         9 Fight Club
        10 Inception
        11 Forrest Gump

DIRECTORS

DIRECTOR_ID NAME
----------- -------------------------
          1 Tim Robbins
          2 Morgan Freeman
          3 Marlon Brando
          4 Al Pachino
          5 Robert De Niro
          6 Christian Bale
          7 Heath Ledger
          8 John Travola
          9 Uma Thurman
         10 Clint Eastwood
         11 Eli Wallach

Direct

MOVIE_ID DIRECTOR_ID
---------- -----------
         1           1
         1           2
         2           3
         2           4
         3           4
         3           5
         4           6
         4           7
         5           8
         5           9
         6          10

I'd like a query that returns all movies that have directors x, y and z as their director:

Example:

If I look for a movie with and , it should return because I don't have a movie that has both of them as it's director. 一起看电影,它应该也不会返回,因为我没有一部以导演和导演兼任的电影。

However if I were to look for a movie with and , it should return 一起看电影,它应该返回

Please suggest alternative if it cannot be done using the above design.

I've attempted it but my query will return results :(

SELECT m.title FROM Movie m 
WHERE m.movie_id IN (
      SELECT d.movie_id FROM Direct d
      WHERE d.director_id IN (
           SELECT director_id FROM Director dir 
           WHERE name IN('Clint Eastwood', 'Al Pachino')));

This is one way to achieve this, viz to group, filter and then count the directors:

SELECT m.title 
FROM Movie m
     INNER JOIN Direct md
     on md.movie_id = m.movie_id
     INNER JOIN Directors d
     on md.director_id = d.director_id
WHERE 
     d.name IN('Clint Eastwood', 'Al Pachino')
GROUP BY m.title
HAVING COUNT(DISTINCT d.director_id) = 2;

SqlFiddle here

{Out of interest, aren't these the actors in the movies?}

With the IN operator, your query returns movies that have any of these directors.

You have to check for each director separately:

SELECT *
FROM Movie
WHERE movie_id IN (SELECT movie_id
                   FROM Direct
                   WHERE director_id = (SELECT director_id
                                        FROM Directors
                                        WHERE name = 'Clint Eastwood'))
  AND movie_id IN (SELECT movie_id
                   FROM Direct
                   WHERE director_id = (SELECT director_id
                                        FROM Directors
                                        WHERE name = 'Al Pachino'))

Alternatively, use a compound query to construct a list of movie IDs for both directors:

SELECT *
FROM Movie
WHERE movie_id IN (SELECT movie_id
                   FROM Direct
                   WHERE director_id = (SELECT director_id
                                        FROM Directors
                                        WHERE name = 'Clint Eastwood')
                   INTERSECT
                   SELECT movie_id
                   FROM Direct
                   WHERE director_id = (SELECT director_id
                                        FROM Directors
                                        WHERE name = 'Al Pachino'))

Alternatively, get all records for these two directors from the Direct table, and then group by the movie to be able to count the directors per movie; we need to have two left:

SELECT *
FROM Movie
WHERE movie_id IN (SELECT movie_id
                   FROM Direct
                   WHERE director_id IN (SELECT director_id
                                         FROM Directors
                                         WHERE name IN ('Clint Eastwood',
                                                        'Al Pachino')
                   GROUP BY movie_id
                   HAVING COUNT(*) = 2)

The shortest one

   ;with cte as (      
    select m.title, ROW_NUMBER() over (partition by m.title order by dir.name) rn 
    from Movie m
    inner join Direct d on m.movie_id = d.movie_id
    inner join Director dir on dir.DIRECTOR_ID = d.director_id
    where dir.name IN ('Clint Eastwood', 'Al Pachino')
    )
    select * from cte where rn > 1

You can try it as well with SQLite:

SELECT title
 FROM movie NATURAL JOIN direct NATURAL JOIN directors
 GROUP BY title
  HAVING GROUP_CONCAT(name)  LIKE '%Morgan Freeman%' AND GROUP_CONCAT(name)  LIKE '%Tim  Robbins%'
;

Test here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM