Найефективніший спосіб групового видалення рядків з postgres


23

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

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

  • Виконайте DELETEзапит для кожного рядка у файлі простим WHEREпервинним ключем (або згрупуйте видалення в групи з nвикористанням IN()пункту)
  • Імпортуйте первинні ключі у тимчасову таблицю за допомогою COPYкоманди, а потім видаліть із головної таблиці за допомогою з'єднання

Будь-які пропозиції будуть дуже вдячні!


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

Я натрапив на це питання, тому що у мене була схожа проблема. Я прибираю базу даних, що містить 300 М + рядків, остаточна база даних матиме лише близько 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.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.