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)

Чини свой Domain на беке. А разово на слонике сам нагуглишь команды.

anonymous
()

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

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

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

Можешь.

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

Можешь.

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

anonymous
()

ChatGPT посоветовал такой запрос, мне понравилось:

DELETE FROM order_clients
WHERE ctid NOT IN (
  SELECT min(ctid)
  FROM order_clients
  GROUP BY order_id, client_id
);
vbr ★★★★★
()

Список условий выглядит как будто это задача с экзамена или собеседования.

anonymous
()

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

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

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

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

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

LINUX-ORG-RU ★★★★★
()

я также не могу использовать ctid

иди отсюда, а…

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

топики ТСа - как легко поднять бабок и не запускается $ИГРА в Стиме.

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

Изначально так и было. Но забраковали запрос. Запрос был как у vbr (похожий).

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

в транзакции взять на неё эксключивный лок

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

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

Пробовал. Такой запрос забраковали.

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

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

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

Такого не бывает чтобы просто «не дадут». «Не дадут» по какой-то причине, либо называй её, либо дадут.

anonymous
()
Ответ на: комментарий от 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
()

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

Это всё равно мало, чувак. Скрипт, который дропнет дубликаты и добавит ограничение на вставку новых, отработает меньше чем за минуту.

hateyoufeel ★★★★★
()

43 миллиона

Это всё равно не очень много. Просто id у строк есть?

no-such-file ★★★★★
()
Ответ на: комментарий от 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)
delete from 
  order_clients oc1
using
  order_clients oc2
where
  (oc1.order_id, oc1.client_id) = (oc2.order_id, oc2.client_id)
  and oc1.linked_at < oc2.linked_at

Ну и, конечно же, добавить ограничение на уникальность/первичный ключ.

theNamelessOne ★★★★★
()

Фига ты возорник)

anonymous
()

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

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

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

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

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

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

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

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

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

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

А, та это порешается, конечно

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

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

Нет))))

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

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

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

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

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

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

В коде будет проверка. Будет констрейнт для уникальной связки order_id и client_id.

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

Я понял анонче. Спасибо за ответ

CryNet ★★★★★
() автор топика
Ответ на: комментарий от 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 ★★★★★
() автор топика
Ответ на: комментарий от monk

Спасибо за готовый запрос. Думаю именно так и сделать.

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

@asdpm

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

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

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

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

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

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

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

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

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

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

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

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

Вот и мне так кажется.

вся суть данной задачи - не основываться на допущениях

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

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

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

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

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

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

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

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

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

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

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

@Loki13 Да. Я опыта набираюсь, мне пока норм. Много нового и интересного.

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

В таблице нет ID, если что.

Это у вас там таблица без PK? Дичь какая.

дичь это суррогатные id PK которые мудаки добавляют бездумно везде

asdpm
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.