mysql — отметить все записи, кроме 1, соответствующие нескольким полям (рядом с дубликатами), как удаленные

#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 Спасибо, что указали на это!