PostgreSQL外键违规的原因是什么?

时间:2015-08-20 08:29:21

标签: postgresql foreign-keys postgresql-9.2 corruption

我的PostgreSQL(9.2)数据库包含两个带有外键约束的表registrationsattributes

postgres=# \d+ registrations;
                Table "public.registrations"
 Column  | Type  | Modifiers | Storage  | Stats target | Description
---------+-------+-----------+----------+--------------+-------------
 name    | text  | not null  | extended |              |
 parent  | text  |           | extended |              |
 storage | bytea |           | extended |              |
Indexes:
    "registrations_pkey" PRIMARY KEY, btree (name)
Referenced by:
    TABLE "attributes" CONSTRAINT "attributes_cname_fkey" FOREIGN KEY (cname) REFERENCES registrations(name) ON DELETE CASCADE
Has OIDs: no

postgres=# \d+ attributes;
                 Table "public.attributes"
 Column | Type  | Modifiers | Storage  | Stats target | Description
--------+-------+-----------+----------+--------------+-------------
 cname  | text  | not null  | extended |              |
 aname  | text  | not null  | extended |              |
 tags   | text  |           | extended |              |
 value  | bytea |           | extended |              |
Indexes:
    "attributes_pkey" PRIMARY KEY, btree (cname, aname)
Foreign-key constraints:
    "attributes_cname_fkey" FOREIGN KEY (cname) REFERENCES registrations(name) ON DELETE CASCADE
Has OIDs: no

在某些时候我意识到有些行违反了外键约束:

postgres=# SELECT COUNT(*) FROM attributes LEFT JOIN registrations ON attributes.cname=registrations.name WHERE registrations.name IS NULL;
 count
-------
    71
(1 row)

你能帮我理解这种腐败是怎么发生的吗?

2 个答案:

答案 0 :(得分:2)

标记为NOT VALID的约束是您可能希望看到违规的一种情况,但NOT VALID子句将显示在psql \d+输出中。 (我相信可以手动更新目录中的这个标志,但我希望你能解决这个问题......)

据我所知,在修改数据之前,唯一支持绕过外键检查的方法是SET session_replication_role TO replica。这是为了复制过程的好处,在假设已经在主服务器上验证了约束的情况下运行 - 尽管如果您的复制器有错误或配置错误,这肯定会出错。

超级用户也可以手动禁用约束的底层触发器(对于试图加速批量导入的人来说,这通常很诱人)。以下内容将告诉您触发器当前是否处于活动状态(tgenabled应为'O'):

SELECT *
FROM pg_trigger
WHERE tgname ~ '^RI_ConstraintTrigger'
  AND tgrelid IN ('registrations'::regclass, 'attributes'::regclass)

我不知道有什么方法可以知道这是否在过去暂时改变了,但是如果你启用了语句记录,你可能会在那里找到ALTER TABLE ... DISABLE TRIGGER语句。

外键执行中也有at least one loophole,当然,你总能发现一个错误......

答案 1 :(得分:0)

如果FK约束是使用NOT VALID条款创建的,则可能会发生这种情况(不要这样做):

CREATE TABLE one
        ( one_id INTEGER NOT NULL PRIMARY KEY
        );

CREATE TABLE two
        ( two_id INTEGER NOT NULL PRIMARY KEY
        , one_id INTEGER NOT NULL
        );

INSERT INTO one(one_id)
SELECT gs FROM generate_series(0,12,2) gs;

INSERT INTO two(two_id,one_id)
SELECT gs, gs FROM generate_series(0,12,3) gs;

ALTER TABLE two
        ADD CONSTRAINT omg FOREIGN KEY (one_id) references one(one_id)
        -- DEFERRABLE INITIALLY DEFERRED
        NOT VALID
        ;