#mysql #sql #duplicates #sql-update #inner-join
#mysql #sql #дубликаты #sql-обновление #внутреннее соединение
Вопрос:
У меня есть таблица mysql для товаров в корзине покупок. Мне нужно пометить все дубликаты одного и того же элемента как удаленные (введя временную метку в столбце «удалено», а не фактически удалить по причинам журнала аудита).
Столбцы таблицы (order_item) являются:
id (int, primary key)
order_id (int)
type (enum)
item_id* (int)
timeslot_id* (int)
price_id* (int)
created (datetime)
deleted (datetime)
Я хочу обновить все, кроме 1, элементы в таблице, которые являются дубликатами (имеют те же значения, что и другая строка), чтобы порядок в столбцах, отмеченных знаком *, имел текущую дату и время в удаленном столбце. Таким образом, одновременно можно приобрести только 1 один и тот же товар.
В настоящее время я добиваюсь этого в несколько этапов, выполняя запрос select с GROUP BY и подсчетом идентификатора и используя Concat для получения списка идентификаторов, разделенных запятыми, а затем обновляя их в отдельном запросе.
SELECT COUNT(id) AS c,
GROUP_CONCAT(DISTINCT id SEPARATOR ",") AS ids
FROM cps_order_item WHERE order_id = "10"
AND deleted = "0000-00-00 00:00:00"
GROUP BY type, item_id, timeslot_id, price_id;
Есть какой-нибудь надежный способ сделать все это в одном запросе?
Ответ №1:
Я думаю, вы можете реализовать логику с обновлением и самосоединением:
update order_item oi
inner join (
select order_id, item_id, timeslot_id, price_id, min(id) as id
from order_item
where deleted = '0000-00-00 00:00:00'
group by order_id, item_id, timeslot_id, price_id
) oi1
on oi1.order_id = oi.order_id
and oi1.item_id = oi.item_id
and oi1.timeslot_id = oi.timeslot_id
and oi1.price_id = oi.price_id
and oi1.id <> oi.id
set oi.deleted = now()
where oi.deleted = '0000-00-00 00:00:00'
Подзапрос вычисляет минимум id
для каждого кортежа значений. Затем внешний запрос задает столбец deleted
для строк, которые совпадают в кортеже и которые id
не являются минимальными.
Это обрабатывает все заказы одновременно. Вы можете добавить фильтр в where
предложение внешнего запроса для обработки только одного заказа. Вы также можете изменить агрегатную функцию на что-то другое, если хотите (может быть, вы хотите max(id)
вместо min(id)
).
Комментарии:
1. Большое спасибо @GMB это было так полезно и сработало (как только я вспомнил, что NULL != NULL в сравнениях mysql).
Ответ №2:
Примечание: Это решение не работает в MySQL. Смотрите Комментарии ниже. Я оставляю это в надежде, что это может помочь кому-то использовать другую СУБД в будущем.
Просто удалите элементы в том же порядке с созданной датой после той, которую вы рассматриваете. Я не совсем уверен в синтаксисе MySQL, но что-то похожее:
UPDATE order_item o
SET deleted = NOW()
WHERE deleted = '0000-00-00 00:00:00'
AND order_id = 10
AND EXISTS (
SELECT *
FROM order_item
WHERE order_id = o.order_id
AND item_id = o.item_id
AND type = o.type
AND timeslot_id = o.timeslot_id
AND price_id = o.price_id
AND deleted = o.deleted
AND created > o.created
);
Я использовал те же ограничения, что и запрос, который вы дали.
Возможно, вы захотите удалить AND ORDER = ID
строку, чтобы очистить все заказы сразу.
Возможно, ограничение на price_id, type и timeslot_id не требуется (при условии, что идентификатор элемента подразумевает эти атрибуты).
Комментарии:
1. Мне также нравится этот ответ, но я просто хочу отметить, что сравнение
created
может быть рискованным (например, одновременно добавлено много записей), поэтому я предпочитаю, чтобы другие ответы использовали MIN(id).2. Вы можете использовать
id > o.id
вместоcreated > o.created
, если считаете, что это безопаснее. Я рад видеть, что ваша проблема решена в любом случае 🙂3. @FabianPijcke: к сожалению, MySQL не поддерживает повторное открытие целевой таблицы в
WHERE
предложении (я знаю, что это база данных с таким ограничением …). Вместо этого вам нужно будет записать это какjoin
запрос.4. Я не знал об этом, я виноват в том, что не пытался выполнить запрос: O Спасибо, что указали на это!