PostgreSQL - отключение ограничений

У меня есть таблица с примерно 5 миллионами строк, в которой есть ограничение fk, ссылающееся на первичный ключ другой таблицы (также примерно 5 миллионов строк).

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

Исходя из опыта Oracle, моей первой мыслью было отключить ограничение, выполнить удаление, а затем снова включить ограничение. PostGres, похоже, позволяет мне отключать триггеры ограничений, если я суперпользователь (я не являюсь, но я вхожу в систему как пользователь, который владеет / создал объекты), но, похоже, это не совсем то, что я хочу.

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

Есть предположения?

edit: после поощрения Билли я попытался выполнить удаление без изменения каких-либо ограничений, и это заняло более 10 минут. Однако я обнаружил, что таблица, из которой я пытаюсь удалить, имеет самореференциальный внешний ключ ... дублированный (& неиндексированный).

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


person azp74    schedule 21.04.2010    source источник
comment
Если это занимает так много времени, даже с 5 миллионами строк, значит, у вас что-то неправильно настроено.   -  person Billy ONeal    schedule 21.04.2010
comment
Какие? Удаление или повторное включение ограничения? И да, вполне возможно, что что-то настроено неправильно или не оптимизировано - база данных в значительной степени была «построена» с помощью спящего режима (я не имел к этому никакого отношения).   -  person azp74    schedule 21.04.2010
comment
Удалить. FK-проверки из индексированных таблиц занимают линейное время, и удаление 75000 + 75000 строк = 150 000 строк. Рассмотрим наихудший случай: 19 сравнений на одну проверку FK (двоичный поиск, lg (5 миллионов) == 19) и, возможно, 20 машинных сравнений на сравнение строк, что эквивалентно 57 000 000 сравнений. Учитывая консервативную оценку средней машины, способной выполнять миллиард сравнений в секунду, это легко, но это все равно должно занимать меньше секунды процессорного времени. Загрузка с диска также не должна быть серьезной проблемой, потому что даже при 5 миллионах строк таблица должна уместиться в ОЗУ.   -  person Billy ONeal    schedule 21.04.2010
comment
Хорошо, Билли, я еще раз попробую прямое удаление ... Я почти уверен, что когда я последний раз пробовал его (это работа, к которой я вернулся через месяц или около того), это было очень медленно.   -  person azp74    schedule 21.04.2010


Ответы (6)


Согласно предыдущим комментариям, это должно быть проблемой. Тем не менее, есть команда, которая может быть тем, что вы ищете - она ​​установит ограничения как отложенные, поэтому они проверяются при COMMIT, а не при каждом удалении. Если вы выполняете только одно большое УДАЛЕНИЕ всех строк, это не будет иметь никакого значения, но если вы делаете это по частям, это будет.

SET CONSTRAINTS ALL DEFERRED

это то, что вы ищете в таком случае. Обратите внимание, что ограничения должны быть помечены как DEFERRABLE, прежде чем их можно будет отложить. Например:

ALTER TABLE table_name
  ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2)
  DEFERRABLE INITIALLY IMMEDIATE;

Затем ограничение можно отложить в транзакции или функции следующим образом:

CREATE OR REPLACE FUNCTION f() RETURNS void AS
$BODY$
BEGIN
  SET CONSTRAINTS ALL DEFERRED;

  -- Code that temporarily violates the constraint...
  -- UPDATE table_name ...
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
person Magnus Hagander    schedule 21.04.2010
comment
Конечно, стоит попробовать, но я не уверен, что отложенные ограничения работают быстрее. Насколько мне известно, они просто переносят работу по проверке со времени DELETE на время COMMIT. - person intgr; 21.04.2010
comment
Я бы попробовал, но удаление fk и восстановление сработало. Как и intgr, мне интересно, не изменит ли он просто проверку fk на время фиксации, поэтому я обязательно запомню это в следующий раз. - person azp74; 22.04.2010
comment
Я удалил базу данных и повторно импортировал ее после запуска SET CONSTRAINTS ALL DEFERRED. Есть ли способ снова включить эти ограничения после завершения импорта? Это довольно большой файл, поэтому будет довольно сложно изменить порядок создания таблицы. Раньше я обходил это, дважды импортируя данные. - person taco; 18.10.2012
comment
Я никогда не пробовал этого, но я пробовал отбрасывать ограничения, а затем повторно добавлять их после, и это было намного быстрее, чем оставлять их, пока я удаляю строки. - person sudo; 13.03.2017

Что сработало для меня, так это отключить одну за другой TRIGGERS из тех таблиц, которые будут задействованы в DELETE операции.

ALTER TABLE reference DISABLE TRIGGER ALL;
DELETE FROM reference WHERE refered_id > 1;
ALTER TABLE reference ENABLE TRIGGER ALL;

Решение работает в версии 9.3.16. В моем случае время выполнения DELETE операций увеличилось с 45 минут до 14 секунд.

Как указано в разделе комментариев @amphetamachine, для выполнения этой задачи вам потребуется admin привилегий для таблиц.

person gersonZaragocin    schedule 24.02.2017
comment
Обратите внимание, что пользователь PostgreSQL, выполняющий команды ALTER TABLE, должен быть владельцем этой таблицы. - person amphetamachine; 04.05.2017
comment
Это также отключит ограничения fk? - person deFreitas; 02.05.2018
comment
Для меня выполнение команды ALTER TABLE было невероятно медленным. - person Wart; 24.08.2018
comment
Это не отключило UNIQUE проверку, иначе я сделал что-то не так (возможно, ограничение UNIQUE не управляется как TRIGGER) - person The Red Pea; 22.11.2018
comment
@TheRedPea: Возможно, это было непреодолимое уникальное ограничение ... на этой странице есть дополнительная информация: Как я могу отключить все ограничения в моем postgresql?: ... Неотложенные ограничения первичного ключа, уникальности и исключения не имеют связанных триггеров и не являются затронутый. - person sol; 24.06.2021

Если вы попробуете DISABLE TRIGGER ALL и получите сообщение об ошибке типа permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger (я получил это на Amazon RDS), попробуйте следующее:

set session_replication_role to replica;

В случае успеха все триггеры, лежащие в основе ограничений таблицы, будут отключены. Теперь вам нужно убедиться, что ваши изменения оставляют БД в согласованном состоянии!

Затем, когда вы закончите, повторно активируйте триггеры и ограничения для вашего сеанса с помощью:

set session_replication_role to default;
person Jonathan Fuerth    schedule 15.01.2018
comment
Это не работает с последней версией PostereSql. - person G.Dealmeida; 06.07.2018
comment
Прекрасно работает с Postgres 12. Обратите внимание, что для установки session_replication_role вам нужно быть суперпользователем. - person eric.green; 07.04.2020

(В этом ответе предполагается, что вы намерены удалить все строки этих таблиц, а не только их выбор.)

Мне тоже пришлось это сделать, но в рамках набора тестов. Я нашел ответ, предложил в другом месте на SO. Используйте TRUNCATE TABLE следующим образом:

TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE];

Следующее быстро удаляет все строки из таблиц table1, table2 и table3, при условии, что нет ссылок на строки этих таблиц из таблиц, не перечисленных:

TRUNCATE TABLE table1, table2, table3;

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

Однако вы можете уточнить запрос, чтобы он также усекал все таблицы со ссылками на перечисленные таблицы (хотя я этого не пробовал):

TRUNCATE TABLE table1, table2, table3 CASCADE;

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

TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY;
person Joe Lapp    schedule 26.09.2016

Мой PostgreSQL - 9.6.8.

set session_replication_role to replica;

работать на меня, но мне нужно разрешение.

Я вхожу в psql с суперпользователем.

sudo -u postgres psql

Затем подключитесь к моей базе данных

\c myDB

И запускаем:

set session_replication_role to replica;

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

person Weekend Baf    schedule 30.10.2018

Отключить все ограничения таблицы

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName

- Включить все ограничения таблицы

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName
person Alam Usmani    schedule 06.04.2015
comment
Вопрос касался Postgresql, у которого нет такой возможности (начиная с версии 9.4). - person Paul R Rogers; 16.04.2015
comment
Согласитесь, что в версии 9.4 нет этой функции. ОШИБКА: синтаксическая ошибка рядом с NOCHECK LINE 1: ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName - person happy_marmoset; 05.08.2015