Удалить с Join в MySQL


501

Вот скрипт для создания моих таблиц:

CREATE TABLE clients (
   client_i INT(11),
   PRIMARY KEY (client_id)
);
CREATE TABLE projects (
   project_id INT(11) UNSIGNED,
   client_id INT(11) UNSIGNED,
   PRIMARY KEY (project_id)
);
CREATE TABLE posts (
   post_id INT(11) UNSIGNED,
   project_id INT(11) UNSIGNED,
   PRIMARY KEY (post_id)
);

В моем PHP-коде при удалении клиента я хочу удалить все сообщения проектов:

DELETE 
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id;

Таблица сообщений не имеет внешний ключ client_id, только project_id. Я хочу удалить сообщения в проектах, которые прошли client_id.

Это не работает сейчас, потому что ни один пост не удален.


10
Я думаю, что ответ Yehosef должен быть принят, так как он использует Join, как вы просили, и что он работает лучше, чем использование предложения IN, как предложил yukondude ...
Херардо Гриньоли

2
Предпочтительным шаблоном является шаблон DELETE posts FROM posts JOIN projects ..., а не IN (subquery)шаблон. (Ответ от Yehosef приводит пример предпочтительного паттерна.)
spencer7593

@ GerardoGrignoli, он работает лучше для определенного движка или версии MySQL? Нет причин, по которым два запроса должны работать по-разному, поскольку AFAIK они идентичны. Конечно, если бы у меня был никель на каждый раз, мой оптимизатор запросов делал что-то глупое…
Пол Дрейпер

Вы также можете использовать aliasдля имени таблицы и использовать это.
biniam

Ответы:


1254

Вам просто нужно указать, что вы хотите удалить записи из postsтаблицы:

DELETE posts
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

РЕДАКТИРОВАТЬ: Для получения дополнительной информации вы можете увидеть этот альтернативный ответ


117
Следует отметить, что это правильный ответ, потому что объединение заставляет вас использовать «УДАЛИТЬ сообщения из сообщений» вместо обычных «УДАЛИТЬ ИЗ сообщений», поскольку таблица для удаления больше не является однозначной. Спасибо!
siannopollo

8
Обратите внимание, что вы не можете использовать метод «как» здесь, например, проекты внутреннего соединения как p на p.project_id ...
zzapper

14
На самом деле вы можете использовать псевдоним для объединенных таблиц, но не для основной таблицы (сообщения). «УДАЛИТЬ ЗАПИСИ ИЗ ПОЛОЖЕНИЙ ВНУТРЕННИХ СОЕДИНЕННЫХ ПРОЕКТОВ p ON p.project_id = posts.project_id»
Weboide


14
это лучший ответ, потому что вы даже можете удалить из обеих таблиц одним действиемDELETE posts , projects FROM posts INNER JOIN projects ON projects.project_id = posts.project_id WHERE projects.client_id = :client_id
Developerium

84

Поскольку вы выбираете несколько таблиц, таблица для удаления больше не является однозначной. Вам необходимо выбрать :

DELETE posts FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

В этом случае table_name1и table_name2те же таблицы, так что это будет работать:

DELETE projects FROM posts INNER JOIN [...]

Вы даже можете удалить из обеих таблиц, если хотите:

DELETE posts, projects FROM posts INNER JOIN [...]

Обратите внимание, что order byи limit не работает для удаления нескольких таблиц .

Также учтите, что если вы объявляете псевдоним для таблицы, вы должны использовать псевдоним при обращении к таблице:

DELETE p FROM posts as p INNER JOIN [...]

Взносы от Carpetsmoker и др .


3
Если вы делаете лучший ответ - по крайней мере, вы можете сделать SQL более читабельным. Все остальные ссылки на SQL в этом вопросе имеют заглавные слова (за исключением одного с 0 голосами). Я не уверен, почему ты отменил мои правки.
Yehosef

3
@ Yehosef, есть группа людей, которые считают, что CAPS действительно блестящие. Я верю, что я не единственный, я видел немало людей, которые тоже использовали строчные буквы.
Pacerier

1
достаточно справедливо - я уважаю ваше право написать свой ответ в том стиле, который вам нравится;)
Yehosef

7
Чтобы добавить к обсуждению заглавные буквы / без заглавных букв, вы можете использовать любой стиль, который вам нравится, но в своем ответе вы фактически смешиваете стили, которые вам удобнее - ONзаглавными. Менее опытным разработчикам может показаться, что это нормально - быть грязным и непоследовательным с точки зрения стиля.
тень

16
Кепки Ключевые слова не являются яркими. они делают Запрашивают читаемые;)
Sachem

50

Или то же самое, с немного другим (более дружелюбным) синтаксисом:

DELETE FROM posts 
USING posts, projects 
WHERE projects.project_id = posts.project_id AND projects.client_id = :client_id;

Кстати, с MySQL использование соединений почти всегда намного быстрее, чем подзапросы ...


Что означает ИСПОЛЬЗОВАНИЕ?
CMCDragonkai

1
Хорошее объяснение USING: stackoverflow.com/questions/11366006/mysql-on-vs-using
bigtex777

10
@ bigtex777: Обратите внимание, что ключевое слово USING в операторах SELECT имеет мало общего с тем же ключевым словом в операторе DELETE. В SELECT он указывает список столбцов, к которым нужно присоединиться, в то время как в DELETE это список всех таблиц в соединении
ivanhoe

42

Вы также можете использовать ALIAS, как это работает, просто использовал его в моей базе данных! т это таблица должна быть удалена из!

DELETE t FROM posts t
INNER JOIN projects p ON t.project_id = p.project_id
AND t.client_id = p.client_id

1
это полезно при соединениях составных ключей, чтобы избежать повторения имен таблиц
Marquez

1
«На самом деле вы можете использовать псевдоним для объединенных таблиц, но не для основной таблицы (записей). 'УДАЛИТЬ ЗАПИСИ ИЗ ПОЛОЖЕНИЙ ВНУТРЕННИЕ СОЕДИНЕННЫЕ ПРОЕКТЫ p ON p.project_id = posts.project_id'" - @ Weboide
Jeaf Gilbert

2
На самом деле (цитата из dev.mysql.com/doc/refman/5.0/en/delete.html ) "Если вы объявляете псевдоним для таблицы, вы должны использовать псевдоним при обращении к таблице: УДАЛИТЬ t1 ОТ теста AS t1, test2 WHERE ... "так что использовать псевдоним нормально.
Питер Бауэрс

25

Я более привык к решению подзапроса, но я не пробовал его в MySQL:

DELETE  FROM posts
WHERE   project_id IN (
            SELECT  project_id
            FROM    projects
            WHERE   client_id = :client_id
        );

85
Вы действительно должны избегать использования ключевого слова IN в SQL (хотя обычно это проще для начинающих) и использовать вместо этого JOIN (когда это возможно), поскольку подзапросы обычно делают вещи намного медленнее.
user276648

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

2
@yukondude Действительно, «IN» гораздо легче понять, чем «JOIN» на первом месте, и поэтому люди, которые не очень хорошо знакомы с SQL, в конечном итоге будут писать «IN» везде, в то время как они могут использовать «JOIN», который работает лучше ( или намного лучше, в зависимости от запроса). Я помню несколько лет назад, что почти все мои SQL-запросы были бы переписаны кем-то, кто действительно знал, как писать хорошие запросы. Вот почему я добавил комментарий, чтобы избежать «IN», чтобы люди знали, что, если возможно, они должны избегать его использования.
user276648

10
Сама причина, по которой я пришел на эту страницу, заключается в том, что запрос, который я написал с оператором IN, был очень медленным. Определенно избегайте принятого ответа здесь.
MikeKulls

4
Сколько бы лет этот вопрос ни был, важно знать, ПОЧЕМУ вы должны использовать JOIN вместо IN. Когда условие выполняется в строке, этот запрос будет выполняться внутри IN. Это означает, что если есть 100 строк, которые необходимо проверить в соответствии с WHERE, этот подзапрос будет выполнен 100 раз. В то время как JOIN будет работать только ОДИН РАЗ. Таким образом, по мере того, как ваша база данных становится все больше и больше, выполнение этого запроса займет все больше и больше времени. @markus только потому, что что-то не критично, не означает, что вы должны писать плохой код. Написание немного лучше сэкономит вам много времени и головной боли в будущем. :)
RisingSun

11

MySQL УДАЛИТЬ записи с JOIN

Обычно вы используете INNER JOIN в операторе SELECT для выбора записей из таблицы, которые имеют соответствующие записи в других таблицах. Мы также можем использовать предложение INNER JOIN с оператором DELETE для удаления записей из таблицы, а также соответствующих записей в других таблицах, например, чтобы удалить записи из таблиц T1 и T2, которые удовлетворяют определенному условию, вы используете следующий оператор:

DELETE T1, T2
FROM T1
INNER JOIN T2 ON T1.key = T2.key
WHERE condition

Обратите внимание, что вы помещаете имена таблиц T1 и T2 между DELETE и FROM. Если вы опустите таблицу T1, оператор DELETE удалит только записи в таблице T2, а если вы опустите таблицу T2, будут удалены только записи в таблице T1.

Условие соединения T1.key = T2.key указывает соответствующие записи в таблице T2, которые необходимо удалить.

Условие в предложении WHERE указывает, какие записи в T1 и T2 необходимо удалить.


11

Удаление одной таблицы:

Чтобы удалить записи из postsтаблицы:

DELETE ps 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

Чтобы удалить записи из projectsтаблицы:

DELETE pj 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

Чтобы удалить записи из clientsтаблицы:

DELETE C
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

Удалить несколько таблиц:

Чтобы удалить записи из нескольких таблиц из объединенных результатов, необходимо указать имена таблиц после DELETE списка, разделенного запятыми:

Предположим , вы хотите удалить записи из всех трех таблиц ( posts, projects, clients) для конкретного клиента:

DELETE C,pj,ps 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id


4

Другой способ удаления с использованием вложенного выбора, который лучше, чем использование IN, будетWHERE EXISTS

DELETE  FROM posts
WHERE   EXISTS ( SELECT  1 
                 FROM    projects
                 WHERE   projects.client_id = posts.client_id);

Одна из причин использовать это вместо объединения - то, что DELETEс JOINзапрещает использование LIMIT. Если вы хотите удалить блоки, чтобы не создавать полных блокировок таблицы, вы можете добавить LIMITиспользовать этот DELETE WHERE EXISTSметод.


1
Может ли этот запрос быть написан с "псевдонимами"? Из синтаксиса не очень понятно, как postsвнутри EXISTS () то же самое, postsчто строки удаляются. (ИМХО в любом случае)
MattBianco

Я слышал, но псевдонимы в таблице не могут быть удалены из. «Посты» в подзапросе должны быть полными именами таблиц, что означает, что если вы хотите повторно использовать эту таблицу в предложении подвыборки «От», вам нужно будет создать там псевдоним.
Джим Клаус

1
Это работает:DELETE p FROM posts p WHERE EXISTS ( SELECT 1 FROM projects WHERE projects.client_id = p.client_id);
MattBianco

4
mysql> INSERT INTO tb1 VALUES(1,1),(2,2),(3,3),(6,60),(7,70),(8,80);

mysql> INSERT INTO tb2 VALUES(1,1),(2,2),(3,3),(4,40),(5,50),(9,90);

УДАЛИТЬ записи из одной таблицы:

mysql> DELETE tb1 FROM tb1,tb2 WHERE tb1.id= tb2.id;

УДАЛИТЬ ЗАПИСИ ИЗ обеих таблиц:

mysql> DELETE tb2,tb1 FROM tb2 JOIN tb1 USING(id);

1

Если объединение не работает для вас, вы можете попробовать это решение. Он предназначен для удаления потерянных записей из t1, когда не используются внешние ключи + определенное условие где. Т.е. он удаляет записи из таблицы 1, которые имеют пустое поле «код» и которые не имеют записей в таблице 2, совпадая по полю «имя».

delete table1 from table1 t1 
    where  t1.code = '' 
    and 0=(select count(t2.name) from table2 t2 where t2.name=t1.name);


-3

- Обратите внимание, что вы не можете использовать псевдоним над таблицей, где вам нужно удалить

DELETE tbl_pagos_activos_usuario
FROM tbl_pagos_activos_usuario, tbl_usuarios b, tbl_facturas c
Where tbl_pagos_activos_usuario.usuario=b.cedula
and tbl_pagos_activos_usuario.cod=c.cod
and tbl_pagos_activos_usuario.rif=c.identificador
and tbl_pagos_activos_usuario.usuario=c.pay_for
and tbl_pagos_activos_usuario.nconfppto=c.nconfppto
and NOT ISNULL(tbl_pagos_activos_usuario.nconfppto)
and c.estatus=50
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.