2008-09-26 53 views
34

谷歌在这一个上的结果有点薄,但建议它不容易。如何在Postgres 8.2中禁用参照完整性?

我的具体问题是,我需要重新编号的ID在涉及彼此,使得B表中有一个“table_a_id”列两个表。我不能先将表A重新编号,因为它的B中的孩子指向旧的ID。我不能将表B重新编号,因为它们在创建之前会指向新的ID。现在重复三或四个表格。

我真的不希望有反复折腾的个人关系时,我可以只“开始交易;禁止参考完整性;标识理清;重新启用参考完整性;提交事务”。 Mysql和MSSQL都提供IIRC功能,所以如果Postgres没有,我会感到惊讶。

谢谢!

+0

请查看:http://www.postgresql.org/docs/8.2/static/sql-set-constraints.html – Victor 2014-10-23 15:20:29

回答

16

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

然而,似乎可以使限制DEFERRABLE,使得它们不检查,直到交易结束。请参阅PostgreSQL documentation for CREATE TABLE(搜索'可推迟',它位于页面中间)。

5

我认为你需要列出你的外键约束,放下它们,做你的修改,然后重新添加约束。检查文档alter table drop constraintalter table add constraint

48

有两件事情可以做(这是相辅相成的,而不是替代):

  • 创建外键约束为可延迟。然后,调用“SET CONSTRAINTS DEFERRED;”,这将导致外键约束不被检查直到事务结束。请注意,如果您不指定任何内容,则默认为NOT DEFERRABLE(烦人)。
  • 呼叫 “ALTER TABLE MYTABLE DISABLE TRIGGER ALL;”,这将阻止而加载的数据的任何的触发器执行的,则 “ALTER TABLE MYTABLE ALL启用触发器;”当你完成重新启用它们时。
+3

从PostgreSQL 9开始,您需要指定要禁用的内容:`ALTER TABLE mytable DISABLE TRIGGER USER;`这将仅禁用用户创建的约束,即:您的FK和PK约束。 – hydrogen 2010-11-03 20:42:56

+0

我需要'禁用触发全部;`以禁用PG9上的FK限制。 – 2011-07-01 00:48:54

+2

ALTER TABLE mytable DISABLE TRIGGER ALL需要超级用户权限; SET CONSTRAINTS DEFERRED必须在事务内执行。 – clapas 2013-08-13 11:05:27

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:[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 
-3

我认为一个easear的解决办法是要创造一个你想他们是“临时”的列关联。

更新与外键的新列

降inicial列

重命名为新的“临时”列相同的名称,则inicial那些值。

31

我发现这两个优秀的脚本,它们会生成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脚本。

一旦你放弃了约束条件,你可以做所有你喜欢的表。当你完成重新介绍他们。

0

如果约束条件是DEFERRABLE,这非常简单。只需使用事务处理块,并将您的FK约束设置为在事务开始时延迟。

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

SET约束将制约当前事务中检查的行为。 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; 

(见)

我认为这种方法比删除和重新创建您的约束更好一些,如某些人已经描述的那样,或者直到需要超级用户权限的事务结束时禁用所有(或全部用户)触发器,如早期的comment by @clapas