Как отключить ссылочную целостность в Postgres 8.2? - PullRequest
35 голосов
/ 26 сентября 2008

Результаты Google немного хуже, но предполагают, что это не легко возможно.

Моя конкретная проблема заключается в том, что мне нужно изменить нумерацию идентификаторов в двух таблицах, которые связаны друг с другом, чтобы в таблице B был столбец "table_a_id". Я не могу перенумеровать таблицу A сначала, потому что потом ее дочерние элементы в B указывают на старые идентификаторы. Я не могу изменить нумерацию таблицы B, потому что тогда они будут указывать на новые идентификаторы до того, как они будут созданы. Теперь повторите для трех или четырех таблиц.

Я действительно не хочу возиться с отдельными отношениями, когда я мог бы просто «начать транзакцию; отключить целостность ссылки; отсортировать идентификаторы; повторно включить целостность ссылки; зафиксировать транзакцию». Mysql и MSSQL оба предоставляют эту функциональность IIRC, поэтому я был бы удивлен, если бы Postgres этого не сделал.

Спасибо!

Ответы [ 7 ]

48 голосов
/ 26 сентября 2008

Вы можете сделать две вещи (это дополняющие, а не альтернативные варианты):

  • Создайте ограничения внешнего ключа как DEFERRABLE. Затем вызовите «SET CONSTRAINTS DEFERRED;», что приведет к тому, что ограничения внешнего ключа не будут проверяться до конца транзакции. Обратите внимание, что по умолчанию, если вы ничего не указали, НЕ ОБЯЗАТЕЛЬНО (досадно).
  • Вызовите «ALTER TABLE mytable DISABLE TRIGGER ALL;», который предотвращает выполнение любых триггеров при загрузке данных, затем «ALTER TABLE mytable ENABLE TRIGGER ALL;» когда вы закончите, чтобы включить их.
33 голосов
/ 29 мая 2012

Я нашел эти 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, необходимые для удаления и создания ограничений.

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

17 голосов
/ 26 сентября 2008

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

Однако кажется, что вы можете установить ограничения DEFERRABLE, чтобы они не проверялись до конца транзакции. См. Документация PostgreSQL для CREATE TABLE (поиск "отложенный", он находится в середине страницы).

5 голосов
/ 29 апреля 2010

Вот скрипт Python, который удалит все ограничения в транзакции, выполнит несколько запросов, а затем заново создаст все эти ограничения. 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:pass@host/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
5 голосов
/ 26 сентября 2008

Я думаю, вам нужно составить список ограничений внешнего ключа, удалить их, внести изменения, а затем снова добавить ограничения. Проверьте документацию для alter table drop constraint и alter table add constraint.

0 голосов
/ 05 мая 2015

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

С http://www.postgresql.org/docs/9.4/static/sql-set-constraints.html:

SET CONSTRAINTS устанавливает поведение проверки ограничений в текущей транзакции. НЕМЕДЛЕННЫЕ ограничения проверяются в конце каждого оператора. Отложенные ограничения не проверяются до момента подтверждения транзакции.

Чтобы вы могли сделать:

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 .

0 голосов
/ 24 января 2011

Я думаю, что более простым решением было бы создание «временных» столбцов, связывающих то, где вы хотите их видеть.

обновить значения с помощью внешних ключей для новых столбцов

отбросить начальные столбцы

переименуйте новые «временные» столбцы с теми же именами, что и исходные.

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