【问题标题】:Disable PostgreSQL foreign key checks for migrations禁用 PostgreSQL 外键检查以进行迁移
【发布时间】:2016-11-01 21:59:01
【问题描述】:

我正在 PostgreSQL 9.4 中创建大量具有外键的迁移。

这让人很头疼,因为这些表在迁移时必须完全按照外键所期望的顺序排列。如果我必须从我的新迁移依赖于外键的其他包运行迁移,它会变得更加棘手。

在 MySQL 中,我可以通过简单地将 SET FOREIGN_KEY_CHECKS = 0; 添加到我的迁移文件顶部来简化此操作。我怎样才能在 PostgresSQL 中暂时只为迁移代码的长度执行此操作?

顺便说一句,为此使用 Laravel Schema Builder。

【问题讨论】:

    标签: postgresql


    【解决方案1】:

    PostgreSQL 不支持任何配置选项,但还有另一种可能。

    postgres=# \d b
            Table "public.b"
    ┌────────┬─────────┬───────────┐
    │ Column │  Type   │ Modifiers │
    ╞════════╪═════════╪═══════════╡
    │ id     │ integer │           │
    └────────┴─────────┴───────────┘
    Foreign-key constraints:
        "b_id_fkey" FOREIGN KEY (id) REFERENCES a(id) DEFERRABLE
    

    Postgres 中的参照完整性是通过触发器实现的,您可以在表上禁用触发器。使用这种方法,您可以上传任何数据(风险),但速度要快得多——因为检查大数据的成本很高。如果你的上传是安全的,那么你就可以做到。

    BEGIN;
    ALTER TABLE b DISABLE TRIGGER ALL;
    -- now the RI over table b is disabled
    ALTER TABLE b ENABLE TRIGGER ALL;
    COMMIT;
    

    下一个可能性是使用延迟约束。这个移动约束检查提交时间。所以你不应该用INSERT命令来尊重顺序:

    ALTER TABLE b ALTER CONSTRAINT b_id_fkey DEFERRABLE;
    
    BEGIN
    postgres=# SET CONSTRAINTS b_id_fkey DEFERRED;
    SET CONSTRAINTS
    postgres=# INSERT INTO b VALUES(100); -- this is not in a table
    INSERT 0 1
    postgres=# INSERT INTO b VALUES(10);
    INSERT 0 1 
    postgres=# COMMIT;
    ERROR:  insert or update on table "b" violates foreign key constraint "b_id_fkey"
    DETAIL:  Key (id)=(100) is not present in table "a".
    

    您应该首选此方法,因为将检查插入的数据。

    【讨论】:

    • 出于某种原因,这对我有用一次,然后根本没有。我在 aws aurora postgres 中,他们锁定了 super user 角色,这样客户就不会弄乱复制设置。看来我必须是超级用户才能禁用某些系统触发器。 (我目前正在使用我的管理员帐户,它也是所有者——我不确定为什么它曾经工作过。)设置复制选项也不是一个可行的选项,因为这也需要super user 角色。我唯一的选择似乎是删除并重新创建外键......
    • 这里也一样。做DISABLE TRIGGER ALL的事情,但是没有效果。我什至没有得到任何警告。它只是被忽略了。
    • 在 Amazon RDS 上,这会产生以下错误:> 权限被拒绝:“RI_ConstraintTrigger_a_23031”是一个系统触发器,所以这个方法并不适用于所有情况,不幸的是:)
    • 本地数据库也出错。用户拥有数据库的所有权限,得到 **permission denied: "RI_ConstraintTrigger_a_16564" is a system trigger **
    • @otocan - 不,还没有。性能是一样的。理论上它可以以不同的方式实现,但没有人做过这项工作。
    【解决方案2】:

    对于迁移,禁用所有触发器更容易:

    SET session_replication_role = 'replica';
    

    迁移后重新启用所有

    SET session_replication_role = 'origin';
    

    【讨论】:

    • 天哪,这是否更简单,更适合手头的特定任务。 (是的。)
    • 注意:这需要超级用户权限。试试“SET CONSTRAINTS ALL DEFERRED”。
    • 我在 10.4 上,上面的声明似乎不起作用。
    • 是否有人能够概述这种方法的危险/风险,应该在什么情况下使用它以及如何降低风险?如果这被认为是不好的做法,最佳做法是什么?
    • 顺便说一句,这个参数可以在AWS RDS的数据库参数组中设置,不用重启db即可应用!如果您将 DMS 用于创建现有架构和约束的空数据库,则非常有用。
    猜你喜欢
    • 1970-01-01
    • 2016-03-21
    • 2021-06-02
    • 2012-04-11
    • 2012-06-03
    • 2016-06-17
    • 1970-01-01
    • 2016-03-23
    • 2019-10-27
    相关资源
    最近更新 更多