Як видалити з select у MySQL?


86

Цей код не працює для MySQL 5.0, як переписати його, щоб він працював

DELETE FROM posts where id=(SELECT id FROM posts GROUP BY id  HAVING ( COUNT(id) > 1 ))

Я хочу видалити стовпці, які не мають унікального ідентифікатора. Додам, що більшу частину часу це єдиний ідентифікатор (я спробував in-синтаксис, і він теж не працює).

Відповіді:


212

SELECT(під) запити повертають набори результатів . Тож вам потрібно використовувати IN, а не =у вашому WHEREреченні.

Крім того, як показано у цій відповіді, ви не можете змінити одну і ту ж таблицю з підзапиту в межах одного запиту. Тим НЕ менше, ви можете SELECTпотім DELETEв окремих запитах, або гніздо іншого підзапиту і псевдонім в результаті внутрішнього підзапиту (виглядає досить Hacky, хоча):

DELETE FROM posts WHERE id IN (
    SELECT * FROM (
        SELECT id FROM posts GROUP BY id HAVING ( COUNT(id) > 1 )
    ) AS p
)

Або використовуйте об’єднання, як запропонував Mchl .


1
У мене була таблиця із 150 дублікатами ключів. Я виконав вищезазначений запит, і там було сказано "144 рядки постраждали", але там де ще є дублікати ключів. Отже, я знову виконав запит, і в ньому написано: 5 рядків, ще раз: 1 рядок. Потім усі дублікати ключів куди зникли. Чому це?
Олексій

Це відбувається, тому що ви SELECT id FROM posts GROUP BY id HAVING ( COUNT(id) > 1 )
вилучаєте

# 1248 - Кожна похідна таблиця повинна мати свій власний псевдонім
Тханг

@thang: Ось чому я сказав псевдонім внутрішнього підзапиту.
BoltClock

1
Поясніть, будь ласка, що означає "Як p"?
Крикет

22
DELETE 
  p1
  FROM posts AS p1 
CROSS JOIN (
  SELECT ID FROM posts GROUP BY id HAVING COUNT(id) > 1
) AS p2
USING (id)

Здається, це працює, але мене бентежить синтаксис і я не можу знайти десь інших ресурсів, щоб пояснити це. CROSS JOINмабуть, виконує декартове приєднання, тож, здається, це може зробити непотрібну роботу або виконати не оптимально? Хтось може пояснити?
wintron

Це зробить декартовий продукт лише за умови відсутності USINGзастереження. При USINGцьому товар обмежується парами, що мають однакове значення в idстовпці, тому насправді дуже обмежено.
Mchl

Чи можете ви зробити те саме з внутрішнім з'єднанням? IEDELETE p1 FROM posts AS p1 INNER JOIN ( SELECT ID FROM posts GROUP BY id HAVING COUNT(id) > 1 ) AS p2 ON p2.ID=p1.ID
Кодос Джонсон

1
@Andrew: Так. Функціонально ці об'єднання абсолютно однакові.
Mchl,

5

Ви можете використовувати внутрішнє з'єднання:

DELETE 
    ps 
FROM 
    posts ps INNER JOIN 
         (SELECT 
           distinct id 
         FROM 
             posts 
         GROUP BY id  
      HAVING COUNT(id) > 1 ) dubids on dubids.id = ps.id  

0

Якщо ви хочете видалити всі дублікати, але по одному з кожного набору дублікатів, це одне рішення:

DELETE posts
FROM posts
LEFT JOIN (
    SELECT id
    FROM posts
    GROUP BY id
    HAVING COUNT(id) = 1

    UNION

    SELECT id
    FROM posts
    GROUP BY id
    HAVING COUNT(id) != 1
) AS duplicate USING (id)
WHERE duplicate.id IS NULL;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.