如何在Postgres 8.2中禁用参照完整性?

san*_*ion 35 postgresql referential-integrity

谷歌的这一结果有点薄,但建议不容易实现.

我的具体问题是我需要在两个彼此相关的表中重新编号ID,以便表B中有一个"table_a_id"列.我不能首先对表A重新编号,因为它的B中的子节点指向旧的ID.我不能首先重新编号表B,因为它们会在创建之前指向新的ID.现在重复三到四张桌子.

当我可以"启动事务;禁用ref完整性;对ID排序;重新启用ref完整性;提交事务"时,我真的不想摆弄个人关系.Mysql和MSSQL都提供了这个功能IIRC,所以如果Postgres没有,我会感到惊讶.

谢谢!

Nic*_*son 47

你可以做两件事(这些是互补的,而不是替代品):

  • 创建外键约束为DEFERRABLE.然后,调用"SET CONSTRAINTS DEFERRED;",这将导致在事务结束之前不检查外键约束.请注意,如果您未指定任何内容,则默认值为NOT DEFERRABLE(烦人).
  • 调用"ALTER TABLE mytable DISABLE TRIGGER ALL;",它可以防止在加载数据时执行任何触发器,然后"ALTER TABLE mytable ENABLE TRIGGER ALL;" 当你完成重新启用它们.

  • 从PostgreSQL 9开始,您需要指定要禁用的内容:`ALTER TABLE mytable DISABLE TRIGGER USER;`这将仅禁用用户创建的约束,即:您的FK和PK约束. (4认同)
  • ALTER TABLE mytable DISABLE TRIGGER ALL需要超级用户权限; SET CONSTRAINTS DEFERRED必须在事务内执行. (2认同)
  • 根据[这个答案](http://stackoverflow.com/a/18709987/1157054),另一种选择是使用`SET session_replication_role = replica;`来禁用会话剩余部分的所有触发器.(还需要超级用户权限.) (2认同)

Dim*_*ris 33

我发现这两个优秀的脚本生成了用于删除约束然后重新创建它们的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
Run Code Online (Sandbox Code Playgroud)

为了重新创造它们

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;
Run Code Online (Sandbox Code Playgroud)

运行这些查询,输出将是您删除和创建约束所需的sql脚本.

删除约束后,您可以使用表格执行所有操作.完成后重新介绍它们.

  • 我稍微修改了这些语句,将我的约束重新创建为"DEFERRABLE",并能够发出`SET CONSTRAINTS ALL DEFERRED`.干杯 (3认同)

Joe*_*ant 17

这似乎不可能.其他建议几乎总是指在完成工作后删除约束并重新创建它们.

但是,似乎您可以制定约束DEFERRABLE,以便在事务结束之前不会检查它们.请参阅PostgreSQL文档CREATE TABLE(搜索"deferrable",它位于页面中间).


Lia*_*iam 5

我认为您需要列出外键约束,删除它们,进行更改,然后再次添加约束.检查的文件alter table drop constraintalter table add constraint.


zzz*_*eek 5

这是一个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
Run Code Online (Sandbox Code Playgroud)

  • 这样的配方仅适用于离线数据迁移操作,因此实际上没有任何"性能"影响. (2认同)