Использование EXISTS вместо оператора IN в операторе удаления с подзапросом - PullRequest
0 голосов
/ 05 июня 2019

Как специалист по SQL, я делал для практики несколько запросов на изменение данных из курса Стэнфорда по SQL. Последнее упражнение было немного сложным для меня, хотя мне удалось его выполнить, используя оператор IN. В качестве небольшого дополнения я попытался переписать запрос, используя EXISTS для подзапроса. Это оказалось сложнее, чем ожидалось, поэтому теперь я задаюсь вопросом, был бы более элегантный способ для этого.

Речь идет о Q4: «Уберите все рейтинги, где год фильма до 1970 года или после 2000 года, а рейтинг меньше 4 звезд».

Упражнения: https://lagunita.stanford.edu/courses/DB/SQL/SelfPaced/courseware/ch-sql/seq-exercise-sql_movie_mod/?activate_block_id=i4x%3A%2F%2FDB%2FSQL%2Fsequential%2Fseq-exercise-sql_movie_mod

Скрипку SQL можно найти здесь: http://www.sqlfiddle.com/#!9/f7f3ad

Заранее большое спасибо!

Сначала я попытался перетащить дополнительный оператор AND в подзапрос и просто заменить IN на EXISTS. Вскоре после того, как я узнал, что это на самом деле удалит всю таблицу, так как предложение Exists будет просто принимать значение true без какой-либо ссылки на внешнюю таблицу.

Поняв это, я попытался использовать псевдоним во внешней таблице, чтобы иметь возможность однозначно ссылаться на внешнюю таблицу изнутри. Однако система не позволит мне сделать это. Я считаю, что они используют Postgres, тогда как мы используем MySQL в Uni. Знаете ли вы, разрешает ли MySQL использовать псевдонимы в операторах изменения данных?

Теперь для самого запроса. Это выглядит довольно многословно для меня. Есть ли более разумный способ сделать это с помощью EXISTS? Или, может быть, что-то совершенно другое, совсем не полагающееся на IN / Exists.

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

Delete 
FROM Rating
Where MId IN 
(
/*movies either before 1970 or after 2000*/
    Select r.MId
    FROM Movie as m
    INNER JOIN Rating as r
    on m.MId = r.MId 
    WHERE m.year <1970 Or m.year > 2000
) 
-- pick those with less than 4 stars
AND stars < 4;

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

 Delete
 FROM Rating
 Where EXISTS(
     Select *
     FROM Movie as m
     INNER JOIN Rating as r
     on m.MId = r.MId 
     WHERE (m.year <1970 Or m.year > 2000) AND r.stars < 4
     AND Rating.rId = r.RId AND Rating.Mid = r.MId AND Rating.stars <4
);

Одна вещь, из-за которой я боролась, это последний бит, где я проверяю, являются ли звезды во внешней таблице <4. Для меня это выглядит так, будто я выполняю всю работу над запросом только в условии соединения. Тем не менее, я обнаружил, что пропуская это, фактически удалил бы все рейтинги с идентификатором 201, поскольку он находит первый кортеж 201, соответствующий критериям (вне диапазона дат, менее 4 звезд), и пришел к выводу, что, поскольку это существует, он также может удалить другой кортеж (в котором звезды на самом деле> = 4)

Ответы [ 2 ]

1 голос
/ 05 июня 2019

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

Я предпочитаю exists:

delete r from rating r
where exists (select 1
                from Movie m
                where m.MId = r.MId and
                      (m.year < 1970 or m.year > 2000)
             ) and
-- pick those with less than 4 stars
     r.stars < 4;

Вы можете сделать что-то похожее с in:

delete r from rating r
where r.MId in (select m.MId
                from Movie m
                where m.year < 1970 or m.year > 2000
             ) and
-- pick those with less than 4 stars
     r.stars < 4;
0 голосов
/ 05 июня 2019

Простая альтернатива с использованием JOIN

DELETE r FROM Rating r
INNER JOIN Movie m ON r.mID=m.mID 
WHERE r.stars<4 AND (m.year<1970 OR m.year>2000);
...