【问题标题】:Postgresql truncate table with foreign key constraintPostgresql 用外键约束截断表
【发布时间】:2019-11-04 03:06:24
【问题描述】:

目前我正在尝试截断在 Postgresql 11.3 上具有外键约束的表。

我试过这样做

BEGIN; 
SET CONSTRAINTS ALL DEFERRED;
TRUNCATE tableA;
COMMIT;

但收到错误

ERROR:  cannot truncate a table referenced in a foreign key constraint
DETAIL:  Table "xxx" references "tableA".
HINT:  Truncate table "xxx" at the same time, or use TRUNCATE ... CASCADE.

SET CONSTRAINTS ALL DEFERRED 不会关闭外键约束检查吗?有没有在不触发外键约束检查且不涉及 CASCADE 的情况下截断表?

【问题讨论】:

    标签: postgresql


    【解决方案1】:

    我来到这里是因为我的 Sqitch 数据恢复脚本使用了 TRUNCATE。一种可能并不适合所有人的解决方法(但对我有用):

    DELETE FROM tableA WHERE id IN (
      '5b50e44a-e8dd-4950-8708-588d6620d578',
      'd1857bfa-43bc-4284-8a49-c6ceba80a66f',
      '3ddff193-eb9f-495b-b63d-87b4e0637caa'
      --etc
    );
    

    【讨论】:

      【解决方案2】:

      您可以执行以下步骤来避免截断期间的外键错误

      1. 创建删除所有外键和约束的自动化脚本(暂时不要运行)

      2. 创建重新创建所有外键和约束的自动化脚本

      3. 运行放置脚本

      4. 运行正常的 TRUNCATE your_table

      5. 运行重新创建密钥脚本

      通过这些步骤,TRUNCATE 命令运行良好,因为没有外键。

      drop 和 re-create 脚本取自 https://blog.hagander.net/automatically-dropping-and-creating-constraints-131/

      删除脚本:

      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;
      

      【讨论】:

      • 这工作得非常好,尽管我确实必须在第 4 步之前和之后执行步骤。在它之前将数据从目标表中复制数据以进行截断(我想保存)到一个临时表中。之后我将保存的数据复制回目标表,然后我还运行了一个脚本来更新外键表,因此只有保留在目标表中的外键仍然设置在相关表中。删除和重新创建脚本非常方便,谢谢!
      【解决方案3】:

      从一个表中删除所有数据

      TRUNCATE TABLE 语句的最简单形式如下:

      TRUNCATE TABLE table_name;
      

      从具有外键引用的表中删除所有数据

      要从主表和所有对主表有外键引用的表中删除数据,请使用CASCADE 选项,如下所示:

      TRUNCATE TABLE table_name CASCADE;
      

      更新:

      BEGIN;
      ALTER TABLE table_name DISABLE TRIGGER ALL;
      TRUNCATE TABLE table_name;
      ALTER TABLE table_name ENABLE TRIGGER ALL;
      COMMIT;
      

      【讨论】:

      • 据我了解,使用 CASCADE 也会影响相关表,这是我不想要的。我想通过忽略可能与其他表有关系的外键约束来截断表。
      • 如果你想这样做,删除外键约束。
      • 您能详细说明您的最终更新吗? DISABLE TRIGGER ALL 做了什么。似乎没有任何改变。
      • 更新是废话。禁用约束只会禁用对单行的约束检查,并且不会禁用在没有级联的情况下执行截断所需的约束本身。 (根据定义截断,与 DELETE FROM 相反,不会逐行处理,因此只会截断表 - 如果与级联一起使用,它甚至可以在现有约束条件下工作,因为它会截断所有相关表)。因此,如果您写 DELETE FROM TABLE table_name; 而不是 Truncate,则更新将是有效的。
      猜你喜欢
      • 2011-07-24
      • 2016-07-28
      • 2019-11-30
      • 1970-01-01
      • 2014-04-06
      • 2011-04-20
      • 1970-01-01
      相关资源
      最近更新 更多