恢复时违反 Postgresql 约束

Postgresql constraint violated at restore

我遇到了一个问题,即在恢复我的数据库时某些行违反了约束,但在使用时从未违反过。

我有 3 个 tables :

CREATE TABLE IF NOT EXISTS "as" (
  "id" bigserial NOT NULL,
  PRIMARY KEY ("id")
);

CREATE TABLE IF NOT EXISTS "bs" (
  "id" bigserial NOT NULL,
  "some_id" bigint,
  PRIMARY KEY ("id")
  FOREIGN KEY ("some_id") REFERENCES "some_table" ("id") 
);

CREATE TABLE IF NOT EXISTS "as_bs" (
    "a_id" bigint NOT NULL,
    "b_id" bigint NOT NULL,
    UNIQUE ("a_id", "b_id"),
    FOREIGN KEY ("a_id") REFERENCES "as" ("id"),
    FOREIGN KEY ("b_id") REFERENCES "bs" ("id")
);

some_table这是另一个table,我认为与这个问题无关。

现在我想要的是通过关系 table as_bsas.idbs.some_id 之间有一个唯一性约束。例如:

INSERT INTO some_table (id) VALUES(1),(2);
INSERT INTO "as" (id) VALUES(1),(2);
INSERT INTO bs (id,some_id) VALUES(1,1),(2,1);
INSERT INTO as_bs (a_id,b_id) VALUES(1,1);
INSERT INTO as_bs (a_id,b_id) VALUES(1,2); -- <<-- Offending row !!!

(感谢@wildplasser 的格式化)

这给我带来了以下约束:

CREATE OR REPLACE FUNCTION check_a_b_some_table_unicity(a_id bigint, b_id bigint)
RETURNS boolean AS
$body$
    BEGIN
        return (SELECT(COUNT(*) = 0)
        FROM as_bs ab
        JOIN bs o1 ON o1.id = ab.b_id
        JOIN bs o2 ON o2.some_id = o1.some_id
        WHERE ab.a_id =  AND o2.id = );
    END;
$body$
LANGUAGE plpgsql;

ALTER TABLE as_bs ADD CONSTRAINT check_a_b_some_table_unicity CHECK (check_a_b_some_table_unicity(a_id, b_id));

然后我的程序就活了下来,但是当我想恢复最近的备份时,由于这个限制我得到了一个错误。

在我的备份中,我删除了约束功能和检查,我可以毫无问题地恢复我的备份。当然,如果我尝试重新申请支票,我会得到:

ERROR:  check constraint "check_a_b_some_table_unicity" is violated by some row

所以我想到了找出哪些行有问题。

为此,我将 asbs 加入 as_bs 并按 unicity 组分组 (a_id, b_id, some_id):

SELECT a_id, b_id, bs.some_id, COUNT(*) occurrences FROM as_bs
JOIN bs ON as_bs.b_id = bs.id
JOIN as ON as_bs.a_id = as.id
GROUP BY a_id, b_id, bs.some_id HAVING COUNT(*) > 1;

令我惊讶的是它没有 return 任何一行...

现在我只是想知道

之间有什么问题

无论如何,我也想知道一个可以注册一次的约束现在为什么不能恢复因为它被违反了。

不确定如何转储和恢复数据库,但这似乎与以下事实有关:当从其他表加载某些数据时,函数引用的记录可能尚不存在。

如果您使用 pg_restore

,请考虑 --disable-triggers 选项

如果您只是加载 sql - 我假设您所有的 CREATE CONSTRAINT TRIGGER 都应该在 sql 文件的末尾 - 如果您这样做,这应该是开箱即用的正在使用 pg_dump 和后来的 psql... database < dump.sql

CREATE TABLE IF NOT EXISTS "as_bs" (
    "a_id" bigint NOT NULL,
    "b_id" bigint NOT NULL,
    UNIQUE ("a_id", "b_id"),
    FOREIGN KEY ("a_id") REFERENCES "as" ("id"),
    FOREIGN KEY ("b_id") REFERENCES "bs" ("id")
);

在您当前的架构中,(as_bs.a_id, as_bs.b_id) 是唯一的 从 asbs 添加依赖列不会使其 更多 唯一。


更新:


\i tmp.sql

CREATE TABLE IF NOT EXISTS "as" (
  "id" bigserial NOT NULL,
  PRIMARY KEY ("id")
);

CREATE TABLE IF NOT EXISTS "some_table" (
  "id" bigserial NOT NULL,
  PRIMARY KEY ("id")
);

CREATE TABLE IF NOT EXISTS "bs" (
  "id" bigserial NOT NULL,
  "some_id" bigint,
  PRIMARY KEY ("id"),
  FOREIGN KEY ("some_id") REFERENCES "some_table" ("id")
);

CREATE TABLE IF NOT EXISTS "as_bs" (
    "a_id" bigint NOT NULL,
    "b_id" bigint NOT NULL,
    UNIQUE ("a_id", "b_id"),
    FOREIGN KEY ("a_id") REFERENCES "as" ("id"),
    FOREIGN KEY ("b_id") REFERENCES "bs" ("id")
);

INSERT INTO some_table (id) VALUES(1),(2);
INSERT INTO "as" (id) VALUES(1),(2);
INSERT INTO bs (id,some_id) VALUES(1,1),(2,1);
INSERT INTO as_bs (a_id,b_id) VALUES(1,1);

CREATE OR REPLACE FUNCTION check_a_b_some_table_unicity(a_id bigint, b_id bigint)
RETURNS boolean AS
$body$
    BEGIN
      return NOT EXISTS ( -- Prefer NOT EXISTS to COUNT(*) < 1
        SELECT *
        FROM as_bs ab
        JOIN bs o1 ON o1.id = ab.b_id
        JOIN bs o2 ON o2.some_id = o1.some_id AND o2.id <> o1.id
        WHERE ab.a_id =  AND o2.id = 
        );
    END;
$body$
LANGUAGE plpgsql;

ALTER TABLE as_bs ADD CONSTRAINT check_a_b_some_table_unicity CHECK (check_a_b_some_table_unicity(a_id, b_id));

INSERT INTO as_bs (a_id,b_id) VALUES(1,2); -- <<-- Offending row !!!
                                                                                                                                                                             

输出:


DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
INSERT 0 2
INSERT 0 2
INSERT 0 2
INSERT 0 1
CREATE FUNCTION
ALTER TABLE
ERROR:  new row for relation "as_bs" violates check constraint "check_a_b_some_table_unicity"
DETAIL:  Failing row contains (1, 2).