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

时间:2020-03-06 14:47:15  来源:igfitidea点击:

Google在这方面的结果有点薄,但是建议这不太容易。

我的特定问题是,我需要重新编号两个相互关联的表中的ID,以使表B中具有" table_a_id"列。我不能先对表A进行重新编号,因为然后表B中的子级指向旧ID。我不能先对表B重新编号,因为在创建新ID之前,它们将指向新ID。现在重复三到四个表。

当我可以"开始事务;禁用引用完整性;整理出ID;重新启用引用完整性;提交事务"时,我真的不想摆弄个人关系。 Mysql和MSSQL都提供IIRC此功能,因此如果Postgres不提供,我会感到惊讶。

谢谢!

解决方案

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

但是,似乎可以将约束设为DEFERRABLE,这样直到事务结束才检查它们。有关CREATE TABLE的信息,请参见PostgreSQL文档(搜索'deferrable',位于页面的中间)。

我认为我们需要列出外键约束的列表,删除它们,进行更改,然后再次添加约束。查看文档中的"变更表删除约束"和"变更表添加约束"。

我们可以做两件事(这些是相辅相成的,不是替代品):

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

这是一个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