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

23

Мне интересно, как наиболее эффективно было бы удалить большое количество строк из PostgreSQL, этот процесс будет частью повторяющейся задачи каждый день для массового импорта данных (дельта вставок + удалений) в таблицу. Могут быть тысячи, потенциально миллионы строк, которые нужно удалить.

У меня есть файл первичных ключей, по одному на строку. Два варианта, о которых я думал, соответствовали приведенному ниже, но я не знаю / не понимаю достаточно внутренних особенностей PostgreSQL, чтобы принять обоснованное решение, которое было бы наилучшим.

  • Выполните DELETEзапрос для каждой строки в файле с простым WHEREпервичным ключом (или сгруппируйте удаления в пакетах, nиспользуя IN()предложение)
  • Импортируйте первичные ключи во временную таблицу с помощью COPYкоманды, а затем удалите из основной таблицы с помощью объединения

Любые предложения будут высоко оценены!

tarnfeld
источник
1
На этот же вопрос был дан более подробный ответ здесь: stackoverflow.com/a/8290958
Саймон

Ответы:

25

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

Способ сделать это состоит в том, чтобы использовать выбор и объединение в вашем удалении.

DELETE FROM foo WHERE id IN (select id from rows_to_delete);

Ни при каких обстоятельствах не следует делать следующее с большой таблицей:

DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);

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

DELETE FROM foo 
WHERE id IN (select id from foo f 
          LEFT JOIN rows_to_keep d on f.id = d.id
              WHERE d.id IS NULL);

PostgreSQL, как правило, довольно хорошо избегает плохих планов, но все же есть случаи, связанные с внешними объединениями, которые могут иметь большое значение для хороших и плохих планов.

Это немного дальше, но я полагаю, что стоит упомянуть, потому что легко перейти от IN к NOT IN и наблюдать за производительностью запросов.

Крис Траверс
источник
Это очень помогло, спасибо! Однако я обнаружил, что использование «комбинирования запросов» более эффективно в данном конкретном случае. Например , IN ( select id from foo except select id from rows_to_keep ) см postgresql.org/docs/9.4/static/queries-union.html
Ufos
1

Я столкнулся с этим вопросом, потому что у меня была похожая проблема. Я очищаю базу данных с 300M + строками, итоговая база данных будет содержать только около 30% исходных данных. Если вы сталкиваетесь с подобным сценарием, на самом деле легче вставить новую таблицу и переиндексировать, а не удалять.

Сделать что-то вроде

CREATE temp_foo as SELECT * FROM foo WHERE 1=2;
INSERT INTO temp_foo (SELECT * FROM foo where foo.id IN (SELECT bar.id FROM BAR);

При правильном индексировании по foo и bar вы можете избежать сканирования Seq.

Тогда вам придется переиндексировать и переименовать таблицу.

Niro
источник