LINUX.ORG.RU

Как удалить дубликаты из таблицы Postgresql?

 


0

1

Таблица:

CREATE TABLE public.order_clients (
  order_id int8 NOT NULL,
  client_id int8 NOT NULL,
  linked_at timestamptz DEFAULT now() NOT NULL,
);

В таблице есть дубликаты, и я хочу удалить не уникальные строки. Связка order_id и client_id должна быть уникальной.

Но это еще не все:

  • ~43 миллиона записей в таблице. В таблицу постоянно что-то записывается

  • я не могу добавить флаг не уникальности в таблицу (таблица используется все время)

  • я также не могу записать уникальные записи в другую таблицу

  • я также не могу использовать ctid (добавляются новые записи, наверное это будет не лучшая идея)

  • я должен использовать limit и where с order_id для постепенного удаления.

Я попытался использовать этот запрос:

DELETE FROM "order_clients"
WHERE ("order_id", "client_id") IN (
 SELECT "order_id", "client_id"
 FROM (
  SELECT "order_id", "client_id", ROW_NUMBER() OVER (PARTITION BY "order_id", "client_id" ORDER BY "order_id" ASC) AS row_num
   FROM "order_clients"
   WHERE "order_id" > 0
  ) AS subquery
WHERE row_num > 1

Однако подзапрос возвращает 315 строк, а весь запрос удаляет 324 строки (остается только один order_id с одним client_id). На бэке Node.JS, можно попробовать как-то на JS порешать.

Есть идеи?

UPD: записей в базе не 43k, а ~43 миллиона

★★★★★

Последнее исправление: CryNet (всего исправлений: 4)

43k записей в таблице. В таблицу постоянно что-то записывается

Это очень мало

я не могу добавить флаг не уникальности в таблицу (таблица используется все время)

Можешь.

я также не могу записать уникальные записи в другую таблицу

Можешь.

У тебя таблица до смешного маленькая, ты можешь в транзакции взять на неё эксключивный лок чтобы все писатели и читатели ждали и ничего не писали, скопировать данные во временную таблицу с group by и обратно, и добавить unique констреинты.

anonymous
()

Мимокрокодил

43k записей в таблице

Я не датабазер :) Но мне кажется что это так мало что можно на живую прям в цикле запустить весь обход базы типа

цикл A обойти все записи базы БАЗА  
   цикл Б обойти все записи БАЗА
      если A == Б то 
        удалить_нахер(A)
      конесли
   концикла
концикла

И это займёт минуту максимум и никто ваще ничего не заметит и не надо никаких мудрёных запросов сооружать. Но играть надо с копией конешна.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от mrjaggers

А если в процессе выполнения скрипта будет добавлена новая запись в эту же табличку на другом конце проекта, это ничем не чревато?

CryNet ★★★★★
() автор топика
Последнее исправление: CryNet (всего исправлений: 1)
Ответ на: комментарий от CryNet

Мне не дали сделать то, что ты разрешил выше (оба раза). То это мне не дадут сделать уж так точно :)

Т.е. дело не в базе, длине таблицы или каких-то технических ограничениях, а в наличии неких «их». Как ты хочешь чтобы форумчане тебе с этим помогли?

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

Ну, все аргументируется постоянной работе с этой таблицей и постоянным добавлением новых записей в неё. Она сильно часто используется. Я понимаю, что выглядит странно. Поэтому хочу понять какие варики у меня ещё есть. Мне вообще понравился такой варик:

DELETE FROM order_clients
  WHERE ctid IN (
    SELECT DISTINCT o.ctid 
      FROM order_clients AS o
      JOIN order_clients AS t
        ON t.client_id = o.client_id AND t.order_id = o.order_id
          AND t.linked_at < o.linked_at
      LIMIT 1000
  );

Но от не знаю какие подводные с использованием ctid.

CryNet ★★★★★
() автор топика
Ответ на: комментарий от CryNet

постоянным добавлением новых записей в неё

Ну так заселекти число новых записей в день, сделай график распределения, найди реальные показатели.

Как вариант скопируй записи, найди дубли в копии и удали записи прицельно.

Ну и да, я правильно понял, что появление новых дублей никого не беспокоит?

Чем тебе лимит поможет?

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 2)
Ответ на: комментарий от CryNet

Ну, все аргументируется постоянной работе с этой таблицей и постоянным добавлением новых записей в неё. Она сильно часто используется. Я понимаю, что выглядит странно.

То что она используется и что туда добавляются записи - не странно. Но это не аргумент против того чтобы за долю секунды транзакционно привести всю таблицу к нужному формату.

anonymous
()
Ответ на: комментарий от CryNet

аргументируется постоянной работе с этой таблицей и постоянным добавлением новых записей в неё

Какие-то надуманные отмазки. Там что реалтайм система управления ядерным реактором что ли? Не помрёт наверное никто за пару минут, пока запрос отработает.

no-such-file ★★★★★
()
Ответ на: комментарий от CryNet

Но от не знаю какие подводные с использованием ctid.

Если у тебя есть linked_at, используй его, а не ctid.

DELETE FROM order_clients
  WHERE (linked_at, order_id, client_id) IN (
    SELECT o.linked_at, o.order_id, o.client_id
      FROM order_clients AS o
      JOIN order_clients AS t
        ON t.client_id = o.client_id AND t.order_id = o.order_id
          AND t.linked_at < o.linked_at
      LIMIT 1000
  );
monk ★★★★★
()

Ну вот под пивом я бы такое запустил несколько раз -

DELETE FROM order_clients
WHERE id IN (
  SELECT min(id)
  FROM order_clients
  GROUP BY min(id), order_id, client_id HAVING count(*) > 1
);

не заморачиваясь

pi11 ★★★★★
()
Последнее исправление: pi11 (всего исправлений: 2)

Если ПО успешно пишет в таблицу дубли, то надо не гланды через жопу вырезать, а чинить ПО и таблицу.

А не давать дебильные задания штудентам.

Тебе 100 раз написали ответ на вопрос из заголовка. И других правильных ответов нет, хотя если ты соискатель, то должен был найти его первым сразу. И не в чатгпт желательно, а понимая, что там делается. Запрос в три строчки епта.

anonymous
()
Ответ на: комментарий от theNamelessOne

этот способ требует чтобы в linked_at были уникальные значения. из задания ОПа нельзя сделать такое допущение, более того из create table скорее можно предполагать обратное

asdpm
()
Ответ на: комментарий от CryNet

Если у тебя есть уникальный id строки этой таблице, и так прямо критично время запроса на удаление, то выбери все дубликаты для удаления во временную таблицу, и удали потом в цикле пройдясь персонально по каждому уникальному id.

Или у тебя там собес\экзамен\испытательный срок и требуют чтобы атомарным запросом обязательно удалялось?

Loki13 ★★★★★
()
Последнее исправление: Loki13 (всего исправлений: 2)
Ответ на: комментарий от no-such-file

Просто id у строк есть?

Нет))))

Это отдельный рофл. ID нет, констрейнта нет. Есть только таблица с дубликатами. Даже в коде проверки нет, чтобы не писать дубли в таблицу

CryNet ★★★★★
() автор топика
Ответ на: комментарий от asdpm

из create table скорее можно предполагать обратное

это почему? чтобы получились одинаковые now() - должны быть два одинаковых INSERT'а в одной транзакции.

Как раз вполне нормально предположить, что двойные записи появляются не из настолько плохого одного клиентского приложения, а из двух разных приложений - почти гарантированные разные now().

Toxo2 ★★★★
()
Ответ на: комментарий от hateyoufeel

А сколько тогда много? Я когда первый раз пробовал удалять дубли этим запросом:

delete from order_clients
where (client_id, order_id, linked_at) not in (
  select client_id, order_id, max(linked_at) as max_linked_at
  from order_clients
  group by client_id, order_id
); 

Узнав, что там 43 миллиона записей, подумал: «ну да, реально, таблица большая, нужны какие-то ограничения».

CryNet ★★★★★
() автор топика
Ответ на: комментарий от Toxo2

@asdpm

У меня в локальной базе есть дубли даже с одинаковой linked_at. Но мне кажется, что это я себе записи продублировал когда что-то тестил. В проде такого быть не должно, т.к. там таска в RabbitMQ отрабатывает и добавляет связку. Т.е в худшем случае хоть какие-то миллисекунды уже будут отличаться.

CryNet ★★★★★
() автор топика
Ответ на: комментарий от Loki13

Или у тебя там собес\экзамен\испытательный срок и требуют чтобы атомарным запросом обязательно удалялось?

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

CryNet ★★★★★
() автор топика
Ответ на: комментарий от Toxo2

это почему? чтобы получились

значения по некоторому набору колонок неуникальны, если нет констрейнта обеспечивающего это. в приведенном в задании ddl много чего есть, но этого констрейнта нет.

должны быть два одинаковых INSERT’а

current_timestamp всегда возвращает одно и то же значение - метку времени на начало транзакции.

вполне нормально предположить, что двойные записи появляются не из настолько плохого одного

чтобы там были неуникальные значения, нужно в одной транзакции дважды внести пару (order_id, client_id). вполне нормально предположить, что скрипты накидавшие туда 43 миллиона записей именно это и делают

asdpm
()
Ответ на: комментарий от CryNet

ID нет, констрейнта нет. Есть только таблица с дубликатами. Даже в коде проверки нет, чтобы не писать дубли в таблицу

Беги оттуда. Я серьёзно. Особенно учитывая какие-то тупейшие отмазки про «низя, оно всегда работает».

no-such-file ★★★★★
()
Ответ на: комментарий от asdpm

в одной транзакции дважды внести пару (order_id, client_id). вполне нормально предположить, что скрипты накидавшие туда 43 миллиона записей именно это и делают

Если бы такое происходило в одном запросе (транзакции) - это невероятно тупейший баг клиента. Почти невозможно себе представить, как такое кто-то наваял. 99 к 100, что это разные запросы/клиенты.

Констрейнты, бывает, и умышленно не ставят/убирают. Когда машина не вывозит слишком большое количество вставок, и целостность и связность вторичны перед потребностью все успевать прожевать - нафиг все лишние CHECKи, которые могут мешать и которыми можно пожертвовать.

Toxo2 ★★★★
()
Ответ на: комментарий от no-such-file

сказать что «таблица постоянно используется» поэтому нельзя тот или иной запрос выполнять мог только некомпетентный ублюдок. потому что в данном случае, контексте, фраза «постоянно используется» не имеет смысла, она может означать что угодно, а значит не означает ничего. компетентный человек если над ней задумается, легко обнаружит, что это так.

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

asdpm
()