MySQL DELETE FROM с подзапросом в качестве условия - PullRequest
73 голосов
/ 17 декабря 2010

Я пытаюсь сделать запрос, подобный этому:

DELETE FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN (
    SELECT DISTINCT(th1.tid)
    FROM term_hierarchy AS th1
    INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
    WHERE th1.parent = 1015
);

Как вы, вероятно, можете сказать, я хочу удалить родительское отношение к 1015, если у того же тида есть другие родители. Однако это приводит к синтаксической ошибке:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS th
WHERE th.parent = 1015 AND th.tid IN (
  SELECT DISTINCT(th1.tid)
  FROM ter' at line 1

Я проверил документацию и сам запустил подзапрос, и все это похоже на проверку. Кто-нибудь может понять, что здесь не так?

Обновление : как указано ниже, MySQL не позволяет использовать таблицу, из которой вы удаляете, в подзапросе для условия.

Ответы [ 8 ]

241 голосов
/ 19 октября 2012

Для тех, кто ищет этот вопрос и хочет удалить его при использовании подзапроса, я оставляю вам этот пример для перехвата MySQL (даже если некоторые люди думают, что это невозможно):

DELETE e.*
FROM tableE e
WHERE id IN (SELECT id
             FROM tableE
             WHERE arg = 1 AND foo = 'bar');

дасту вас ошибка:

ERROR 1093 (HY000): You can't specify target table 'e' for update in FROM clause

Однако этот запрос:

DELETE e.*
FROM tableE e
WHERE id IN (SELECT id
             FROM (SELECT id
                   FROM tableE
                   WHERE arg = 1 AND foo = 'bar') x);

будет работать просто отлично:

Query OK, 1 row affected (3.91 sec)

Оберните ваш подзапрос в дополнительный подзапрос (здесьс именем x) и MySQL с радостью выполнит то, что вы просите.

32 голосов
/ 17 декабря 2010

Псевдоним должен быть включен после ключевого слова DELETE:

DELETE th
FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN 
(
    SELECT DISTINCT(th1.tid)
    FROM term_hierarchy AS th1
    INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
    WHERE th1.parent = 1015
);
32 голосов
/ 17 декабря 2010

Нельзя указать целевую таблицу для удаления.

Обходной путь

create table term_hierarchy_backup (tid int(10)); <- check data type

insert into term_hierarchy_backup 
SELECT DISTINCT(th1.tid)
FROM term_hierarchy AS th1
INNER JOIN term_hierarchy AS th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th1.parent = 1015;

DELETE FROM term_hierarchy AS th
WHERE th.parent = 1015 AND th.tid IN (select tid from term_hierarchy_backup);
7 голосов
/ 17 декабря 2010

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

DELETE th FROM term_hierarchy AS th
....

Как указано здесь в документации по MySQL.

6 голосов
/ 01 августа 2014

Я подошел к этому немного по-другому, и это сработало для меня;

Мне нужно было удалить secure_links из моей таблицы, которая ссылалась на таблицу conditions, где больше не осталось строк условий.Домашний сценарий в основном.Это дало мне ошибку - Вы не можете указать целевую таблицу для удаления.

Поэтому, ища вдохновение, я пришел к следующему запросу, и он работает просто отлично.Это потому, что он создает временную таблицу sl1, которая используется в качестве ссылки для удаления.

DELETE FROM `secure_links` WHERE `secure_links`.`link_id` IN 
            (
            SELECT
                `sl1`.`link_id` 
            FROM 
                (
                SELECT 

                    `sl2`.`link_id` 

                FROM 
                    `secure_links` AS `sl2` 
                    LEFT JOIN `conditions` ON `conditions`.`job` = `sl2`.`job` 

                WHERE 

                    `sl2`.`action` = 'something' AND 
                    `conditions`.`ref` IS NULL 
                ) AS `sl1`
            )

У меня работает.

3 голосов
/ 11 ноября 2015

Не является ли предложение in в delete ... куда крайне неэффективным, если из подзапроса будет возвращено большое количество значений?Не уверен, почему бы вам не просто выполнить внутреннее (или правое) соединение с исходной таблицей из подзапроса идентификатора для удаления, а не с «in (подзапросом)».?

DELETE T FROM Target AS T
RIGHT JOIN (full subquery already listed for the in() clause in answers above) ` AS TT ON (TT.ID = T.ID)

И, возможно,на него есть ответ в «MySQL не позволяет», однако, он работает нормально для меня. ПРЕДОСТАВЛЕНО, я уверен, чтобы полностью уточнить, что удалить (УДАЛИТЬ T ИЗ Target AS T). Удалить с помощью соединения в MySQL устраняет проблему УДАЛИТЬ / СОЕДИНИТЬ.

1 голос
/ 25 декабря 2017

Если вы хотите сделать это с 2 запросами, вы всегда можете сделать что-то похожее на это:

1) получить идентификаторы из таблицы с помощью:

SELECT group_concat(id) as csv_result FROM your_table WHERE whatever = 'test' ...

Затем скопируйте результат смышь / клавиатура или язык программирования ниже XXX:

2) DELETE FROM your_table WHERE id IN ( XXX )

Возможно, вы могли бы сделать это одним запросом, но это то, что я предпочитаю.

0 голосов
/ 11 февраля 2019

@ CodeReaper, @BennyHill: Работает как положено.

Тем не менее, мне интересно сложность времени для миллионов строк в таблице? По-видимому, потребовалось около 5ms для выполнения 5k записей в правильно проиндексированной таблице.

Мой запрос:

SET status = '1'
WHERE id IN (
    SELECT id
    FROM (
      SELECT c2.id FROM clusters as c2
      WHERE c2.assign_to_user_id IS NOT NULL
        AND c2.id NOT IN (
         SELECT c1.id FROM clusters AS c1
           LEFT JOIN cluster_flags as cf on c1.last_flag_id = cf.id
           LEFT JOIN flag_types as ft on ft.id = cf.flag_type_id
         WHERE ft.slug = 'closed'
         )
      ) x)```

Or is there something we can improve on my query above?
...