如何禁用Postgres 8.2中的引用完整性?
谷歌在这个结果是有点薄,但build议,这是不容易的。
我的具体问题是,我需要重新编号在两个相互关联的表中的ID,这样表B中有一个“table_a_id”列。 我不能先把表A重新编号,因为它的B中的孩子指向旧的ID。 我不能重新编号表B,因为他们会在创build之前指向新的ID。 现在重复三,四张表。
当我可以“开始交易;禁用引用完整性;对ID进行sorting;重新启用引用完整性;提交事务”时,我并不想真正摆脱个人关系。 Mysql和MSSQL都提供了这个functionIIRC,所以如果Postgres没有,我会感到惊讶。
谢谢!
这似乎不可能。 其他build议几乎总是指删除约束条件并在完成工作后重新创build它们。
但是,似乎您可以使DEFERRABLE
成为约束,以便在事务结束之前不检查它们。 查看PostgreSQL的CREATE TABLE
文档 (search'推迟',它在页面中间)。
有两件事你可以做(这是互补的,而不是替代品):
- 将您的外键约束创build为DEFERRABLE。 然后,调用“SET CONSTRAINTS DEFERRED;”,这将导致外键约束不被检查直到事务结束。 请注意,如果您不指定任何内容,则默认为NOT DEFERRABLE(烦人)。
- 调用“ALTER TABLE mytable DISABLE TRIGGER ALL;”,这会阻止在加载数据时执行任何触发器,然后执行“ALTER TABLE mytable ENABLE TRIGGER ALL;” 当你完成重新启用它们。
我发现这两个优秀的脚本生成的sql删除约束,然后重新创build它们。 他们来了:
删除约束
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
重新创build它们
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;
运行这些查询,输出将是您需要删除和创build约束的sql脚本。
一旦你放弃限制,你可以做所有你喜欢的表。 当你完成重新介绍他们。
我认为你需要列出你的外键约束,放下它们,做你的修改,然后重新添加约束条件。 检查alter table drop constraint
和alter table add constraint
的文档。
这是一个Python脚本,它将删除事务中的所有约束,运行一些查询,然后重新创build所有这些约束。 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
如果约束是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 。)
我认为这种方法比删除和重新创build您的约束更好,或者在事务结束之前禁用所有(或所有用户)触发器,这需要超级用户权限,正如在@clapas之前的评论中指出的那样。
我认为一个解决scheme就是创build“临时”栏目,把你想要的地方联系起来。
使用外键将值更新到新列
删除inicial列
重命名为新的“临时”列相同的名称,然后是官方的。