Django 1.2 Каскадное удаление PostgreSQL для ключей с ON DELETE NO ACTION - PullRequest
4 голосов
/ 06 сентября 2011

У меня есть база данных postgresql с около 150 таблицами (это проект Django 1.2). Django добавляет ON DELETE NO ACTION и ON UPDATE NO ACTION к внешним ключам во время создания таблицы.

Теперь мне нужно массово удалить данные (около 800 000 записей) из нескольких таблиц на основе определенных условий.

Использование Model.objects.filter().delete() не вариант, потому что данные огромны и занимают много времени.

Только каскадные опции кажутся каскадным удалением, но так как Django добавил «ON DELETE NO ACTION», это похоже на вариант no.

Итак, мой вопрос: есть ли способ заменить все внешние ключи на ON DELETE CASCADE простым способом (их много) или что-то подобное.

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

Ответы [ 3 ]

1 голос
/ 30 апреля 2012

Как указано в ссылке, которая содержит ответ Эндрю, если вы установите для этого параметра значение CASCADE в Django, то Django пойдет и удалит "retail" Если он установлен на NO ACTION, вы можете создать определение внешнего ключа на уровне базы данных для обработки вещей. Это звучит как разумный план для меня.

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

0 голосов
/ 01 октября 2012

Одно примечание: ON DELETE CASCADE плохо работает с массовыми операциями.Причина в том, что это делается как триггер.Следовательно, с алгоритмической точки зрения это выглядит так:

for row in delete_set:
    for dependent row in (scan for referencing rows):
         delete dependent row

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

Лучший способ сделать это - использовать записываемое общее табличное выражение версии 9.1 или выше или простосделать отдельные операторы удаления в той же транзакции.Что-то вроде:

WITH rows_to_delete (id) AS (
     SELECT id FROM mytable WHERE where_condition
),
deleted_rows (id) AS (
     DELETE FROM referencing_table WHERE mytable_id IN (select id FROM rows_to_delete)
     RETURNING mytable_id
),
DELETE FROM mytable WHERE id IN (select id FROM deleted_rows);

Это сводится к чему-то вроде, алгоритмически:

сканирование строк для удаления как delete_set для зависимых при сканировании строк, зависимых от удаления: удаление зависимых для to_delete при сканированиистроки, на которые ссылаются удаленные зависимости: delete to_delete

Избавление от принудительного сканирования вложенных циклов значительно ускорит процесс.

0 голосов
/ 22 февраля 2012
...