MySQL DELETE FROM із підзапитом як умовою


85

Я намагаюся зробити такий запит:

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


2
Увага : Гарна відповідь внизу stackoverflow.com/a/4471359/956397 просто додайте псевдонім таблиці післяDELETE t FROM table t ...
PiTheNumber

Відповіді:


38

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

Обхідний шлях

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.
Roman Newaza,

1
Я можу підтвердити, що це неможливо в 2019 році на MariaDB 10.3.14 або MySQL Community Server 5.7.27
alpham8,

283

Для інших, хто вважає, що це питання хоче видалити під час використання підзапиту, я залишаю вам цей приклад перехитрити 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 із задоволенням виконає те, що ви запитаєте.


10
Зайняв деякий час, але я змусив його працювати. Важливо: 1) Перша таблиця повинна бути псевдонімом, як показано тут "e", 2) "x" в кінці не є заповнювачем, це псевдонім для тимчасової таблиці, що створюється підзапитом "(ВИБЕРІТЬ ідентифікатор з таблиціE ДЕ arg = 1 І foo = 'бар') ".
Тілман Хаушерр

3
Чому це працює? Це для мене дуже змінюється, але, крім того, це не повинно працювати. Це справді працює, але не повинно.
donatJ

1
неймовірно. це насправді працює! але ви не змушені псевдонім таблиці використовувати e ... Ви можете використовувати будь-який псевдонім, який хочете.
Андрій Сандулеску,

1
@jakabadambalazs Мої міркування, коли його придумували, полягали в тому, що підзапит, що починається з "SELECT id", закінчує і повертає список ідентифікаторів, а отже, звільняє замок таблиці, з якої ви хочете видалити.
CodeReaper

9
@jakabadambalazs: Ми не можемо використовувати одну і ту ж таблицю ( e) у DELETE та в її під-SELECT. Однак ми можемо використовувати під-під-ВИБІР для створення тимчасової таблиці ( x), і використовувати його для під-ВИБІР.
Steve Almond

39

Псевдонім слід включати після 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
Це хороша відповідь. Правильна псевдонімізація допоможе вирішити проблеми, подібні до оригінальної публікації. (як і моя.)
usumoio

10

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

DELETE th FROM term_hierarchy AS th
....

Як зазначено тут у документах MySQL.


мова не про псевдонім, перевірте ще раз 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
Björn

1
вам не потрібно виправляти псевдонім, просто не вказуйте цільову таблицю для вибору при видаленні ... це справжня проблема
ajreal

7

Я підійшов до цього дещо по-іншому, і це в мене спрацювало;

Мені потрібно було видалити 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`
            )

Працює для мене.


5

Хіба речення "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 не дозволяє", однак, він працює нормально для мене ЗАБЕЗПЕЧЕНО Я обов'язково повністю роз'яснюю, що видаляти (ВИДАЛИТИ З ЦІЛІ ЯК Т). Видалення за допомогою Join в MySQL роз'яснює проблему DELETE / JOIN.


2

Якщо ви хочете зробити це за допомогою 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було виконати за наявність 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?

0

таким чином ви можете використовувати псевдонім у операторі delete

DELETE  th.*
FROM term_hierarchy th
INNER JOIN term_hierarchy th2 ON (th1.tid = th2.tid AND th2.parent != 1015)
WHERE th.parent = 1015;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.