#mysql #group-by #sql-delete #sqlperformance
#mysql #группировать по #sql-удалить #производительность sql
Вопрос:
У меня есть таблица «История» с примерно 300.000 строками, которая ежедневно заполняется новыми данными. Я хочу сохранить только последние две строки каждой комбинации refSchema / refId.
На самом деле я иду по этому пути:
Первый шаг:
SELECT refSchema,refId FROM History GROUP BY refSchema,refId
С помощью этого оператора я получаю все комбинации (которых около 40.000).
Второй шаг:
Я запускаю foreach, который ищет существующие строки для приведенного выше запроса следующим образом:
SELECT id
FROM History
WHERE refSchema = ? AND refId = ? AND state = 'done'
ORDER BY importedAt
DESC LIMIT 2,2000
Пожалуйста, имейте в виду, что я хочу сохранить последние две строки в своей таблице, поэтому я ограничиваю 2,2000. Если я нахожу совпадающие строки, я помещаю идентификаторы в массив с именем idList .
Заключительный шаг
Таким образом я удаляю все идентификаторы из массива:
DELETE FROM History WHERE id in ($idList)
Все это, похоже, не самая лучшая производительность, потому что я должен проверять каждую комбинацию с помощью дополнительного запроса. Есть ли способ иметь один оператор delete, который делает волшебство, чтобы избежать дополнительных запросов 40.000?
Редактировать Обновление: я использую AWS Aurora DB
Ответ №1:
Если вы используете MySQL 8 , то один концептуально простой способ продолжить здесь — использовать CTE для определения двух верхних строк для каждой группы, которые вы хотите сохранить. Затем удалите любую запись, пара схема / идентификатор которой не отображается в этом белом списке:
WITH cte AS (
SELECT refSchema, refId
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY refSchema, refId ORDER BY importedAt DESC) rn
FROM History
) t
WHERE rn IN (1, 2)
)
DELETE
FROM History
WHERE (refSchema, refId) NOT IN (SELECT refSchema, refId FROM cte);
Если вы не можете использовать CTE, попробуйте встроить вышеупомянутый CTE:
DELETE
FROM History
WHERE (refSchema, refId) NOT IN (
SELECT refSchema, refId
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY refSchema, refId ORDER BY importedAt DESC) rn
FROM History
) t
WHERE rn IN (1, 2)
);
Комментарии:
1. К сожалению, здесь мы используем AWS Aurora… так что там невозможно использовать CTE
2. @Marco Тогда просто попробуйте встроить CTE непосредственно в запрос на удаление.
3. Не разрешается выполнять УДАЛЕНИЕ в подзапросе в той же таблице, например, УДАЛИТЬ ИЗ истории, ГДЕ указан идентификатор (ВЫБЕРИТЕ идентификатор из истории, ГДЕ …)
4. @Marco Да, если вы обернете подзапрос в другой подзапрос, как я сделал выше. Если вы действительно получаете сообщение об ошибке, вставьте его сюда, и я могу попробовать отредактировать свой ответ.
5. Я получаю следующую ошибку: SQL Fehler (1064): У вас ошибка в синтаксисе SQL; проверьте руководство, соответствующее вашей версии сервера MySQL, на предмет правильного синтаксиса для использования рядом с ‘(РАЗДЕЛ ПО refSchema, ПОРЯДОК изменения ПО importedAt DESC) rn ИЗ истории’ в строке 7