Удаление записей в MySQL WHERE id IN (@VARIABLE) (2,3,4)

Есть ли способ удалить записи с помощью WHERE IN @VARIABLE?

-- DEMO TABLE
CREATE TABLE people (
    id int AUTO_INCREMENT NOT NULL, 
    name varchar(100),
    age int,
    active smallint DEFAULT 0,
    PRIMARY KEY (id)
);

-- DEMO DATA
INSERT INTO people(id, name, age, active) 
VALUES
(1, 'Jon', 37, 1),
(2, 'Jack', 23, 0),
(3, 'Peter', 24, 0),
(4, 'Phil', 55, 0);

Создать переменную:

SELECT @REMOVE := GROUP_CONCAT(id) FROM people WHERE active < 1; -- (2,3,4)

Я пытаюсь удалить конкатенированные переменные из строки.

DELETE FROM people WHERE id IN(@REMOVE); -- will delete only first id which is id nr 2

Приведенный выше SQL удаляет только первый элемент из списка. В этом примере список будет содержать: (2,3,4). Будет удалена только запись с id = 2. Записи с id 3, 4 останутся в таблице. См. таблицу до и после на изображении ниже:

введите здесь описание изображения

Я хорошо знаю, что мог бы использовать одно из двух решений, таких как:

Подзапрос:

-- SOLUTION 1 - USEING NESTED SELECT SUB QUERY WITH AN ALIAS 
DELETE FROM people WHERE id IN(SELECT * FROM (SELECT id FROM people WHERE active < 1) as temp);

Решение 1 не идеально, если нам нужно запустить тот же подзапрос в другом запросе на более позднем этапе, желая сохранить исходный вывод при выполнении операций вставки, обновления или удаления в той же таблице.

or

Временная таблица:

CREATE TEMPORARY TABLE temp_remove_people (id int NOT NULL PRIMARY KEY);
INSERT INTO temp_remove_people SELECT id FROM people WHERE active < 1;
DELETE FROM people WHERE id IN(SELECT id FROM temp_remove_people);

Это сохранит исходный выбор в рамках одного сеанса.

Я хотел бы знать, можно ли использовать конкатенированную переменную каким-то другим способом, чтобы заставить ее работать.


person DevWL    schedule 03.07.2021    source источник
comment
Я в замешательстве, почему не может работать первое альтернативное решение, которое масштабируется лучше, чем group_concat? Кроме того, вы используете не внешний SELECT * FROM. Можете ли вы показать проблему с данными или SQL?   -  person Parfait    schedule 03.07.2021
comment
В основном, если бы у меня было две таблицы, связанные по идентификатору, и если мне нужно запросить первую таблицу, чтобы получить набор идентификаторов, затем удалить данные из этой таблицы, а затем попытаться удалить данные из другой таблицы, используя выбор из первой таблицы - он вернется разные результаты, так как я бы уже изменил первую таблицу, из которой я выбираю. Поэтому мне нужно сохранить запись выбранного идентификатора, чтобы я мог ссылаться на него при выполнении запроса в другой таблице. Этот пример только иллюстрирует проблему с удалением из sql-переменной grup_concat, но моя проблема глубже. Временная таблица также решает эту проблему.   -  person DevWL    schedule 03.07.2021
comment
Похоже, у вас есть несколько таблиц people, которые могут быть проблемой дизайна базы данных и, следовательно, проблемой обслуживания. Если можете, попробуйте нормализовать таблицы по различным отношениям. Обычно сложные запросы являются признаком проблем с дизайном.   -  person Parfait    schedule 03.07.2021
comment
@Parfait в этой базе данных более 150 таблиц, и они довольно хорошо нормализованы, так что это не так. Спасибо за твою заботу.   -  person DevWL    schedule 03.07.2021
comment
На самом деле, множество таблиц не обязательно указывает на нормализацию. И снова иметь только одно уникальное отношение: один Люди, один Заказ, один Продукт и т. д. Не используйте элементы данных в качестве объектов таблицы (например, , AmazonTable, GoogleTable, MicrosoftTable...) Например, AdventureWorks (образец базы данных SQL Server), используемый во многих курсах по базам данных, поддерживает более 70 таблиц в нормализованных отдельных объектах в четких схемах (например, базы данных MySQL). Нет избыточности или повторяющихся объектов. Удачи!   -  person Parfait    schedule 03.07.2021


Ответы (3)


Предложение FIND_IN_SET() лишает возможности оптимизировать этот запрос с помощью индекса.

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

Для этого вы должны использовать подготовленный оператор:

SET @sql = CONCAT('DELETE FROM people WHERE id IN(', @REMOVE, ')');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
person Bill Karwin    schedule 03.07.2021
comment
Вы решили это. Спасибо за помощь и указание на недостаток FIND_IN_SET(). Работает как шарм. - person DevWL; 04.07.2021

Список, разделенный запятыми, возвращаемый GROUP_CONCAT(), представляет собой строку, и вы можете использовать такую ​​функцию, как FIND_IN_SET(), чтобы проверить наличие значения в этой строке:

SET @REMOVE = (SELECT GROUP_CONCAT(id) FROM people WHERE active < 1);

DELETE FROM people 
WHERE FIND_IN_SET(id, @REMOVE);

См. демонстрацию.

person forpas    schedule 03.07.2021

Вы можете использовать find_in_set():

where find_in_set(id, @remove) > 0

Однако я подвергаю сомнению весь ваш подход. Вы храните идентификаторы в строках, а идентификаторы изначально являются числами. Это плохо.

Вместо этого просто сохраните значения как временную таблицу, а не строку. Затем вы можете использовать таблицу с in или exists:

where exists (select 1
              from tempids t
              where t.id = p.id
             );

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

person Gordon Linoff    schedule 03.07.2021
comment
Есть ли разница в производительности при запуске DELETE FROM people WHERE id IN(SELECT id FROM temp_remove_people); vs УДАЛИТЬ ИЗ людей p, где существует (выберите 1 из temp_remove_people t, где t.id = p.id ); ? - person DevWL; 04.07.2021
comment
И зачем нам › 0 в том месте, где find_in_set(id, @remove) › 0 - person DevWL; 04.07.2021
comment
@DevWL. . . Для ясности. Технически это будет работать без > 0, но find_in_set() возвращает позицию, в которой найдено значение, поэтому > 0 делает более понятным, что значение найдено. - person Gordon Linoff; 05.07.2021