具有外键约束的关系数据库如何摄取可能顺序错误的数据?

How can a relational database with foreign key constraints ingest data that may be in the wrong order?

数据库正在从流中摄取数据,满足外键约束所需的所有行可能迟到或永远不会到达。

这可能通过使用另一个数据存储来实现,一个没有外键约束的数据存储,然后当所有需要的数据可用时,读入具有 fk 约束的数据库。然而,这增加了复杂性,我想避免它。

我们正在研究一种解决方案,该解决方案创建 "placeholder" 行以将外键指向。当真正的数据进来时,占位符被替换为真正的值。同样,这增加了复杂性,但这是我们迄今为止找到的最佳解决方案。

人们通常如何解决这个问题?

编辑:一些示例数据可能有助于解释问题:

假设我们有这些表:

CREATE TABLE order (
  id INTEGER NOT NULL,
  order_number,
  PRIMARY KEY (id),
  UNIQUE (order_number)
);

CREATE TABLE line_item (
  id INTEGER NOT NULL,
  order_number INTEGER REFERENCES order(order_number),
  PRIMARY KEY (id)
);

如果我先插入一个订单,没问题!但是假设我尝试:

INSERT INTO line_item (order_number) values (123) 在插入订单 123 之前。这当然会使 fk 约束失败。但这可能是我获取数据的顺序,因为它是从从多个来源收集数据的流中读取的。

此外,为了解决@philpxy 的问题,我在这方面并没有找到太多。提到的一件事是 deferred constraints。这是一种等待在事务结束时执行 fk 约束的机制。但是,我认为在我的情况下不可能这样做,因为只要收到数据,这些插入语句就会随机 运行。

您遇到了业务工作流问题,因为在订单本身进入之前,单个订单的行项目进入。一个可能不理想的解决方法是创建一个插入前触发器,它会检查每个传入插入到 line_item table,该命令是否已存在于 order table。如果没有,那么它会先插入订单记录,然后再尝试插入 line_item.

CREATE OR REPLACE FUNCTION "public"."fn_insert_order" () RETURNS trigger AS $$
BEGIN
    INSERT INTO "order" (order_number)
    SELECT NEW.order_number
    WHERE NOT EXISTS (SELECT 1 FROM "order" WHERE order_number = NEW.order_number);
    RETURN NEW;
END
$$
LANGUAGE 'plpgsql'

# trigger 
CREATE TRIGGER "trigger_insert_order"
BEFORE INSERT ON line_item FOR EACH ROW
EXECUTE PROCEDURE fn_insert_order()

注意:我假设 order table 的 id 列实际上是自动递增的,在这种情况下,Postgres 会在插入时自动为其赋值多于。很可能,这就是您想要的,因为有两个都需要手动分配的 id 列没有多大意义。

您可以通过 line_item 上的 BEFORE INSERT 触发器来实现。 在该触发器中,您查询 order 是否存在匹配项,如果不存在,则插入一个虚拟行。

这将允许 INSERT 成功,但会牺牲一些性能。

要将行插入 order,请使用

INSERT INTO order ...
ON CONFLICT ON (order_number) DO UPDATE SET
   id = EXCLUDED.id;

更新主键有问题,可能会导致冲突。解决这个问题的一种方法是,如果您对人工生成的订单使用负数 id(假设真实的 id 是正数)。如果您对该主键有任何引用,则必须使用 ON UPDATE CASCADE.

定义约束