Как эффективно удалить строки из таблицы Postgresql 8.1? - PullRequest
6 голосов
/ 22 апреля 2009

Я работаю над сценарием PostgreSQL 8.1 SQL, который должен удалить большое количество строк из таблицы.

Допустим, таблица, из которой мне нужно удалить, это Employees (~ 260K строк). У него есть первичный ключ с именем id.

Строки, которые мне нужно удалить из этой таблицы, хранятся в отдельной временной таблице EmployeesToDelete (~ 10K записей) со ссылкой на внешний ключ Employees.id, называемой employee_id.

Есть ли эффективный способ сделать это?

Сначала я подумал о следующем:

DELETE
FROM    Employees
WHERE   id IN
        (
        SELECT  employee_id
        FROM    EmployeesToDelete
        )

Но я слышал, что использование предложения IN и подзапросов может быть неэффективным, особенно для больших таблиц.

Я посмотрел документацию по PostgreSQL 8.1, и там есть упоминание о УДАЛИТЬ ОТ ... ИСПОЛЬЗОВАНИЯ, но у него нет примеров, поэтому я не уверен, как его использовать.

Мне интересно, работает ли следующее и более ли оно эффективно?

DELETE
FROM    Employees
USING   Employees e
INNER JOIN
        EmployeesToDelete ed
ON      e.id = ed.employee_id

Ваши комментарии очень ценятся.

Edit: Я запустил EXPLAIN ANALYZE, и странно то, что первый DELETE работал довольно быстро (в течение нескольких секунд), в то время как второй DELETE занял так много времени (более 20 минут), и я в конце концов отменил его.

Добавление индекса во временную таблицу значительно улучшило производительность.

Вот план запроса первого УДАЛИТЬ для всех, кто интересуется:

 Hash Join  (cost=184.64..7854.69 rows=256482 width=6) (actual time=54.089..660.788 rows=27295 loops=1)
   Hash Cond: ("outer".id = "inner".employee_id)
   ->  Seq Scan on Employees  (cost=0.00..3822.82 rows=256482 width=10) (actual time=15.218..351.978 rows=256482 loops=1)
   ->  Hash  (cost=184.14..184.14 rows=200 width=4) (actual time=38.807..38.807 rows=10731 loops=1)
         ->  HashAggregate  (cost=182.14..184.14 rows=200 width=4) (actual time=19.801..28.773 rows=10731 loops=1)
               ->  Seq Scan on EmployeesToDelete  (cost=0.00..155.31 rows=10731 width=4) (actual time=0.005..9.062 rows=10731 loops=1)

 Total runtime: 935.316 ms
(7 rows)

На этом этапе я буду придерживаться первого УДАЛЕНИЯ, если не найду лучшего способа его написания.

Ответы [ 4 ]

10 голосов
/ 22 апреля 2009

Не угадывай, мери. Попробуйте различные методы и посмотрите, какой из них самый короткий для выполнения. Также, используйте EXPLAIN , чтобы узнать, что будет делать PostgreSQL, и посмотрите, где вы можете оптимизировать. Очень немногие пользователи PostgreSQL могут угадать правильно самый быстрый запрос ...

7 голосов
/ 22 апреля 2009

Мне интересно, работает ли следующее и более ли оно эффективно?

    DELETE
    FROM    Employees e
    USING   EmployeesToDelete ed
    WHERE   id = ed.employee_id;

Это полностью зависит от вашей селективности индекса.

PostgreSQL имеет тенденцию использовать MERGE IN JOIN для IN предикатов, который имеет стабильное время выполнения.

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

Для упорядоченного набора результатов требуется либо операция сортировки, либо индекс. Полный обход индекса очень неэффективен в PostgreSQL по сравнению с SEQ SCAN.

Предикат JOIN, с другой стороны, может выиграть от использования NESTED LOOPS, если ваш индекс очень избирателен, и от использования HASH JOIN, если он не селективен.

PostgreSQL следует выбрать правильный, оценивая количество строк.

Поскольку у вас есть 30k строк против 260K строк, я ожидаю, что HASH JOIN будет более эффективным, и вы должны попытаться построить план по запросу DELETE ... USING.

Чтобы убедиться, пожалуйста, опубликуйте план выполнения для обоих запросов.

2 голосов
/ 22 апреля 2009

Я не уверен насчет DELETE FROM ... USING syntax, но, как правило, подзапрос должен логически совпадать с INNER JOIN в любом случае. Оптимизатор запросов к базе данных должен быть способен (и это только предположение) выполнять один и тот же план запросов для обоих.

0 голосов
/ 22 апреля 2009

Почему вы не можете сначала удалить строки вместо их добавления в таблицу EmployeesToDelete?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...