2016-10-19 14 views
0

В MySQL я хочу удалить строки в таблице t1 и все связанные строки в таблице t2, где выполняется какое-либо условие (например, некоторый статус).Использование нескольких DELETE в MySQL вместо JOIN DELETE

Я знаю, что могу это сделать:

DELETE t1 FROM t1 INNER JOIN t2 
WHERE t1.some_id = t2.some_id 
AND t1.status=3; 

Но есть некоторые повисшие данные в t2, не ссылается t1, и я хочу, чтобы удалить это тоже (это и старый разбитый DB). Я думаю, что я мог бы сделать внешнее соединение для этого, а UNION - с внутренним соединением. Но это становится более сложным, потому что мне нужно удалить строки примерно в 10 таблицах, все связанные с идентификаторами. Конечным результатом был сумасшедший большой запрос.

Так что я привел в этом:

DELETE FROM t1 
WHERE status=3; 

DELETE FROM t2 
WHERE some_id NOT IN (SELECT some_ID FROM t1) 

DELETE FROM t3 
WHERE some_id NOT IN (SELECT some_ID FROM t2) 

и так далее для всех таблиц. Таким образом, все несвязанные данные удаляются, и это более читаемо. Обратите внимание, что внешних ключей нет, и я не могу изменять таблицы (так что забудьте о «каскадном удалении»).

Итак, это неправильно? Каковы недостатки этого, как я это сделал?

Благодаря

Edit: таблицы большие, t1 имеет полтора миллиона строк, t2 имеет 3 миллионы строк, в 8 других таблицах менее полторы миллионов. Но я также удаляю много данных, около 75%.

Редактировать 2: Хорошо, я покажу вам настоящую вещь, а не только пример. Целью этого является удаление около 250000 старых билетов в osTicket (после их резервного копирования).

Работа в виртуальной машине, я сначала попытался это:

DELETE ticket, thread, event, cdata, user, email, email_info 
FROM  ost_ticket   AS ticket 
INNER JOIN ost_ticket_thread  AS thread 
INNER JOIN ost_ticket_event  AS event 
INNER JOIN ost_ticket__cdata  AS cdata 
INNER JOIN ost_user    AS user 
INNER JOIN ost_user_email  AS email 
INNER JOIN ost_ticket_email_info AS email_info 
WHERE ticket.ticket_id = thread.ticket_id 
AND ticket.ticket_id = event.ticket_id 
AND ticket.ticket_id = cdata.ticket_id 
AND ticket.user_id = user.id 
AND ticket.user_id = email.user_id 
AND thread.id  = email_info.thread_id 
AND ticket.status_id=3 
AND (ticket.closed < DATE_SUB(NOW(), INTERVAL 30 DAY)); 

Это удаляет только 7 таблиц, мне нужно добавить по крайней мере, 2-3 таблицы к нему. Может больше. Это займет 15 минут, чтобы запустить и оставляет много несвязанных данных.

Тогда я пошел с этим (в обновленном VM)

START TRANSACTION; 
DELETE FROM ost_ticket WHERE status_id=3 AND (closed < DATE_SUB(NOW(), INTERVAL 30 DAY)); 
DELETE FROM ost_ticket_thread WHERE ticket_id NOT IN (SELECT ticket_id FROM ost_ticket); 
DELETE FROM ost_ticket_event WHERE ticket_id NOT IN (SELECT ticket_id FROM ost_ticket); 
DELETE FROM ost_ticket__cdata WHERE ticket_id NOT IN (SELECT ticket_id FROM ost_ticket); 
DELETE FROM ost_user WHERE id NOT IN (SELECT DISTINCT user_id FROM ost_ticket); 
DELETE FROM ost_user_email WHERE user_id NOT IN (SELECT id FROM ost_user); 
DELETE FROM ost_ticket_email_info WHERE thread_id NOT IN (SELECT id FROM ost_ticket_thread); 
COMMIT; 

Он принимает все данные, и занимает 8 минут, чтобы бежать.

+0

Единственный недостаток, который приходит на ум, заключается в том, что вы не делаете это как атомную транзакцию, если это имеет значение. Учитывая, что возраст/состояние базы данных звучит так, как целостность транзакции не производится или не прерывается. Возможно, у других есть идеи. – mba12

+0

Я не вижу проблемы с этим. – CptMisery

+0

Имеются удаления нескольких таблиц. [docs] (http://dev.mysql.com/doc/refman/5.7/en/delete.html) – Uueerdo

ответ

0

Неплохо сделать это в 3-х отдельных DELETE запросах. Плюсы:

  • более читаемым
  • это Безразлично `делать тяжелые объединения (если таблицы большие) - в некоторых случаях он может выполнять быстрее

Минусы:

  • если таблицы большие, то второй и третий запросы могут быть медленными, потому что выбор подзапроса возвращает много данных ....
  • вы получите удаление в 3 шага вместо одного - что означает, что у вас будут данные в t2 и t3 после первого запроса на удаление, а в t3 после второго удаления ....которые в зависимости от применения, могут быть хорошими или не

Я бы посоветовал вам для более оптимального пути:

DELETE FROM t3 
WHERE some_id IN (SELECT some_ID FROM t2 
WHERE some_id IN (SELECT some_ID FROM t1 WHERE status=3)) 

DELETE FROM t2 
WHERE some_id IN (SELECT some_ID FROM t1 WHERE status=3) 

DELETE FROM t1 
WHERE status=3; 

Если удаление меньше строк в t1 чем строк левой в t1 (после удаления) этот заказ будет быстрее. Не забудьте удалить из t1 в последнем запросе, потому что указанное выше удаление зависит от t1 :). Но этот порядок действителен только в том случае, если это какой-то вопрос о каскадном удалении (t1 строки, которые должны быть удалены, связаны с t2 и t3)

+1

Я не уверен, но я не думаю, что вы хотите, чтобы '' НЕ IN' на 't3' удалялось при выполнении запросов в этом порядке; вы? – Uueerdo

+0

Да, спасибо, я забыл изменить его. Теперь это нормально. – krasipenkov

+0

Это не так уж плохо с 3 столами, но с примерно 10 таблицами он становится тяжелым. – SomeDudeWithCode

0

Что-то особенно не так с использованием синтаксиса удаления нескольких таблиц, подобных этому?

DELETE t1, t2, t3 
FROM t1 
INNER JOIN t2 ON t1.some_ID = t2.some_ID 
INNER JOIN t3 ON t2.some_ID = t3.some_ID 
WHERE t1.status=3 
; 

Я знаю, что если у вас есть внешние ключи, это может иногда вызывать проблемы с MySQL, удаляющими ссылочные строки перед ссылками на строки; но вы заявили, что у вас их нет.