Как отключить ссылочную целостность в Postgres 8.2?
Результаты Google по этому вопросу немного тонкие, но предполагают, что это непросто.
Моя конкретная проблема заключается в том, что мне нужно перенумеровать идентификаторы в двух таблицах, связанных друг с другом, так что в таблице B есть столбец "table_a_id". Я не могу перенумеровать таблицу A сначала, потому что тогда ее дети в B указывают на старые идентификаторы. Я не могу перенумеровать таблицу B сначала, потому что тогда они будут указывать на новые идентификаторы до их создания. Теперь повторите три или четыре таблицы.
На самом деле я не хочу заниматься индивидуальными отношениями, когда я могу просто "начать транзакцию, отключить целостность ссылок, вывести идентификаторы сортировки, снова включить целостность ref, совершить транзакцию". Mysql и MSSQL обеспечивают эту функциональность IIRC, поэтому я был бы удивлен, если Postgres этого не сделал.
Спасибо!
Ответы
Ответ 1
Это не представляется возможным. Другие предложения почти всегда относятся к уменьшению ограничений и их повторному созданию после завершения работы.
Однако, похоже, вы можете создавать ограничения DEFERRABLE
, чтобы они не проверялись до конца транзакции. См. Документация PostgreSQL для CREATE TABLE
(найдите "отложенные", это посередине страницы).
Ответ 2
Есть две вещи, которые вы можете сделать (они дополняют друг друга, а не альтернативы):
- Создайте ограничения внешнего ключа как DEFERRABLE. Затем вызовите "SET CONSTRAINTS DEFERRED", что приведет к тому, что ограничения внешнего ключа не будут проверяться до конца транзакции. Обратите внимание, что значение по умолчанию, если вы ничего не указываете, НЕ ДЕФРИРУЕТ (досадно).
- Вызов "ALTER TABLE mytable DISABLE TRIGGER ALL;", который предотвращает выполнение любых триггеров во время загрузки данных, затем "ALTER TABLE mytable ENABLE TRIGGER ALL"; когда вы закончите, чтобы снова включить их.
Ответ 3
Я нашел эти 2 превосходных скрипта, которые генерируют sql для удаления ограничений, а затем воссоздают их. вот они:
Для удаления ограничений
SELECT 'ALTER TABLE "'||nspname||'"."'||relname||'" DROP CONSTRAINT "'||conname||'";'
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END,contype,nspname,relname,conname
Для их воссоздания
SELECT 'ALTER TABLE "'||nspname||'"."'||relname||'" ADD CONSTRAINT "'||conname||'" '|| pg_get_constraintdef(pg_constraint.oid)||';'
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END DESC,contype DESC,nspname DESC,relname DESC,conname DESC;
Запустите эти запросы, и вывод будет скриптами sql, которые вам нужны для удаления и создания ограничений.
Как только вы отмените ограничения, вы можете делать все, что хотите, с помощью таблиц. Когда вы закончите, повторно представите их.
Ответ 4
Я думаю, вам нужно составить список ограничений внешнего ключа, отбросить их, внести свои изменения и снова добавить ограничения. Проверьте документацию на alter table drop constraint
и alter table add constraint
.
Ответ 5
Здесь Python script, который удалит все ограничения в транзакции, запустит некоторые запросы, а затем воссоздает все эти ограничения. pg_get_constraintdef
делает это сверхлегким:
class no_constraints(object):
def __init__(self, connection):
self.connection = connection
def __enter__(self):
self.transaction = self.connection.begin()
try:
self._drop_constraints()
except:
self.transaction.rollback()
raise
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is not None:
self.transaction.rollback()
else:
try:
self._create_constraints()
self.transaction.commit()
except:
self.transaction.rollback()
raise
def _drop_constraints(self):
self._constraints = self._all_constraints()
for schemaname, tablename, name, def_ in self._constraints:
self.connection.execute('ALTER TABLE "%s.%s" DROP CONSTRAINT %s' % (schemaname, tablename, name))
def _create_constraints(self):
for schemaname, tablename, name, def_ in self._constraints:
self.connection.execute('ALTER TABLE "%s.%s" ADD CONSTRAINT %s %s' % (schamename, tablename, name, def_))
def _all_constraints(self):
return self.connection.execute("""
SELECT n.nspname AS schemaname, c.relname, conname, pg_get_constraintdef(r.oid, false) as condef
FROM pg_constraint r, pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE r.contype = 'f'
and r.conrelid=c.oid
""").fetchall()
if __name__ == '__main__':
# example usage
from sqlalchemy import create_engine
engine = create_engine('postgresql://user:[email protected]/dbname', echo=True)
conn = engine.connect()
with no_contraints(conn):
r = conn.execute("delete from table1")
print "%d rows affected" % r.rowcount
r = conn.execute("delete from table2")
print "%d rows affected" % r.rowcount
Ответ 6
Если ограничения DEFERRABLE
, это очень просто. Просто используйте блок транзакций и установите ограничения FK для отсрочки в начале транзакции.
От http://www.postgresql.org/docs/9.4/static/sql-set-constraints.html:
SET CONSTRAINTS задает поведение проверки ограничений в текущей транзакции. Ограничения IMMEDIATE проверяются в конце каждого оператора. Ограничения DEFERRED не проверяются до фиксации транзакции.
Итак, вы можете сделать:
BEGIN;
SET CONSTRAINTS
table_1_parent_id_foreign,
table_2_parent_id_foreign,
-- etc
DEFERRED;
-- do all your renumbering
COMMIT;
К сожалению, кажется, что Postgres по умолчанию задает все ограничения NOT DEFERRABLE
, если явно не задано DEFERRABLE
. (Я предполагаю, что это по соображениям производительности, но я не уверен.) Что касается Postgres 9.4, то не сложно изменить ограничения, чтобы сделать их отложенными при необходимости:
ALTER TABLE table_1 ALTER CONSTRAINT table_1_parent_id_foreign DEFERRABLE;
(См. http://www.postgresql.org/docs/9.4/static/sql-altertable.html.)
Я думаю, что этот подход был бы предпочтительнее, если бы вы сбросили и воссоздали свои ограничения, как описано некоторыми, или отключить все триггеры (или все пользовательские) до конца транзакции, что требует привилегий суперпользователя, как указано в более раннем комментарий от @clapas.
Ответ 7
Я думаю, что простым решением было бы создать "временные" столбцы, связывающие, где вы хотите.
обновить значения с помощью внешних ключей для новых столбцов
отбрасывать столбцы в столбце
переименуйте в новые "временные" столбцы с теми же именами, а затем на официальные.