MySQL DELETE FROM с подзапросом в качестве условия

87

Я пытаюсь сделать такой запрос:

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, если у того же tid есть другие родители. Однако это дает мне синтаксическую ошибку:

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 не позволяет использовать таблицу, которую вы удаляете, в подзапросе для условия.

микл
источник
3
Внимание : хороший ответ внизу stackoverflow.com/a/4471359/956397 просто добавьте псевдоним таблицы послеDELETE t FROM table t ...
PiTheNumber

Ответы:

40

Вы не можете указать целевую таблицу для удаления.

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

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);
аджреал
источник
мы оба правы - см. его комментарий к моему ответу ниже. Синтаксис псевдонима и логика были проблемой :)
JNK
Да, похоже, удаление с помощью подзапроса в MySQL в настоящее время невозможно - спасибо, что взглянули на него :)
mikl
разве "УДАЛИТЬ ИЗ term_hierarchy AS th" в последней строке не имеет той же проблемы? Я получаю такую ​​же синтаксическую ошибку, как OP.
malhal
Вы должны добавить Индекс в term_hierarchy_backup.tid.
Роман Newaza 02
1
Я могу проверить это, что в 2019 году это невозможно на MariaDB 10.3.14 или MySQL Community Server 5.7.27
alpham8
292

Для тех, кто считает этот вопрос желаемым удалить при использовании подзапроса, я оставляю вам этот пример для перехвата 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 с радостью сделает то, что вы просите.

CodeReaper
источник
10
Потребовалось некоторое время, но я заставил его работать. Важно: 1) Первая таблица должна иметь псевдоним, как показано здесь с помощью «e», 2) «x» в конце не является заполнителем, это псевдоним для временной таблицы, созданной подзапросом »(SELECT id FROM tableE ГДЕ arg = 1 И foo = 'bar') ".
Tilman Hausherr
3
Почему это работает? Это очень меняет для меня, но более того, работать не должно. Это делает работу, но это не должно быть.
donatJ
1
невероятно. это действительно работает! но вы не обязаны использовать псевдоним таблицы с помощью e ... вы можете использовать любой псевдоним, который хотите.
Андрей Сандулеску
1
@jakabadambalazs Придумывая это, я руководствовался тем, что подзапрос, начинающийся с «SELECT id», завершается и возвращает список идентификаторов и, следовательно, снимает блокировку таблицы, из которой вы хотите удалить.
CodeReaper 09
9
@jakabadambalazs: мы не можем использовать одну и ту же таблицу ( e) в DELETE и в его под-SELECT. Мы можем , однако , использовать суб-суб-SELECT , чтобы создать временную таблицу ( x), и использование , что для суб-SELECT.
Стив Алмонд
40

Псевдоним должен быть включен после 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
);
Джеймс Уайзман
источник
3
Это хороший ответ. Правильное сглаживание будет иметь большое значение для решения проблем, аналогичных исходному посту. (как у
меня
11

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

DELETE th FROM term_hierarchy AS th
....

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

JNK
источник
не о псевдониме, пожалуйста, проверьте OP еще раз
ajreal
@ajreal - Я сделал, и обратите внимание, что ошибка начинается с определения псевдонима, а в документации MySQL явно указано, что вам нужно использовать псевдоним в операторе DELETE, а также в предложении FROM. Тем не менее, спасибо за отрицательный голос.
JNK
просто сделай это, delete from your_table as t1 where t1.id in(select t2.id from your_table t2);что у тебя получилось?
Ajreal
4
В документации четко указано; Currently, you cannot delete from a table and select from the same table in a subquery. dev.mysql.com/doc/refman/5.5/en/delete.html
Бьёрн
1
вам не нужно исправлять псевдоним, просто не указывайте целевую таблицу для выбора при удалении ... это настоящая проблема
аджреал
7

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

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

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

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

Работает на меня.

Даррен Эдвардс
источник
Дубликат из @CodeReaper выше ... хороший звонок ...;)
jrypkahauer
5

Разве предложение "in" в удалении ... где, крайне неэффективно, если из подзапроса будет возвращено большое количество значений? Не уверен, почему бы вам не просто внутренне (или правильно) присоединиться к исходной таблице из подзапроса по идентификатору для удаления, а не использовать «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 проясняет проблему DELETE / JOIN.

Джефф
источник
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

@CodeReaper, @BennyHill: Работает, как ожидалось.

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

Мой запрос:

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?
rc.adhikari
источник
0

вы можете использовать псевдоним таким образом в операторе удаления

DELETE  th.*
FROM term_hierarchy th
INNER JOIN term_hierarchy th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th.parent = 1015;
ЯковГдл35
источник