如何在 Postgres 8.2 中禁用参照完整性?
Posted
技术标签:
【中文标题】如何在 Postgres 8.2 中禁用参照完整性?【英文标题】:How do I disable referential integrity in Postgres 8.2? 【发布时间】:2008-09-26 14:29:06 【问题描述】:Google 在这方面的搜索结果有点少,但表明这并不容易。
我的具体问题是我需要重新编号两个相互关联的表中的 ID,以便表 B 中有一个“table_a_id”列。我不能先重新编号表 A,因为 B 中的子代指向旧 ID。我不能先对表 B 重新编号,因为它们会在创建之前指向新的 ID。现在重复三到四张桌子。
当我可以“开始事务;禁用 ref 完整性;整理 ID;重新启用 ref 完整性;提交事务”时,我真的不想摆弄个人关系。 mysql 和 MSSQL 都提供此功能 IIRC,所以如果 Postgres 没有,我会感到惊讶。
谢谢!
【问题讨论】:
看看这个:postgresql.org/docs/8.2/static/sql-set-constraints.html 【参考方案1】:您可以做两件事(它们是互补的,而不是替代品):
将外键约束创建为 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 约束。
我需要 DISABLE TRIGGER ALL;
来禁用 PG9 上的 FK 约束。
ALTER TABLE mytable DISABLE TRIGGER ALL 需要超级用户权限; SET CONSTRAINTS DEFERRED 必须在事务内部执行。
根据this answer,另一个选项是使用SET session_replication_role = replica;
禁用会话剩余部分的所有触发器。 (也需要超级用户权限。)
在 9.x 中是 SET CONSTRAINTS ALL DEFERRED
。【参考方案2】:
我发现这 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 脚本。
一旦你放弃了约束,你就可以对表格做任何你喜欢的事情。完成后重新介绍它们。
【讨论】:
我对这些语句稍作修改,将我的约束重新创建为DEFERRABLE
,并能够发出SET CONSTRAINTS ALL DEFERRED
。干杯
非常感谢。这节省了我的时间。
这真的很有趣。我编辑了您的答案以在实体名称周围添加引号,以使其与非小写实体兼容。无论如何,我不明白为什么在重新创建约束时排序降序,这似乎根本不需要。【参考方案3】:
这似乎不可能。其他建议几乎总是提到删除约束并在工作完成后重新创建它们。
但是,您似乎可以设置约束DEFERRABLE
,以便在事务结束之前不会检查它们。请参阅PostgreSQL documentation for CREATE TABLE
(搜索“可延期”,它位于页面中间)。
【讨论】:
【参考方案4】:我认为您需要列出外键约束,删除它们,进行更改,然后再次添加约束。查看alter table drop constraint
和alter table add constraint
的文档。
【讨论】:
【参考方案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
【讨论】:
这种方法对性能有何影响?对我来说听起来非常昂贵。 这样的配方仅适用于离线数据迁移操作,因此实际上并没有任何“性能”影响。【参考方案6】:如果约束是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。)
我认为这种方法比某些人描述的删除和重新创建您的约束更可取,或者禁用所有(或所有用户)触发器直到事务结束,这需要超级用户权限,如前面的 @987654323 所述@。
【讨论】:
【参考方案7】:我认为一个简单的解决方案是创建“临时”列,将它们关联到您希望它们所在的位置。
使用新列的外键更新值
删除初始列
将新的“临时”列重命名为与初始列相同的名称。
【讨论】:
-1 不是对原始海报问题的回答。此外,提议的解决方案似乎不是一个好主意 (TM)。以上是关于如何在 Postgres 8.2 中禁用参照完整性?的主要内容,如果未能解决你的问题,请参考以下文章
CentOS 8.2使用pgAdmin安装PostgreSQL
使用 Perl,如何在保持参照完整性的同时从单个表加载多个表?