外键有额外的约束?

Nis*_*ant 7 postgresql foreign-key database-design constraint check-constraints

有一个名为Item(id, name, cost)and的表Orders(id, bill_id, item_id, units),用于跟踪所下的订单,其中相同的bill_id表示它属于单个订单。

如果需要在Order表中添加item_id,如何在 DB 中施加一个额外的约束,表明Item应该是“可用的”(在那个时间点)?在这种情况下,项目被手动确定为“可用”并且不能从数据库中的其他字段派生。

一种模式设计(我更喜欢)是添加一个类型列,该列将具有“可用”和“不可用”字段。但是如何检查外键约束item_id不仅应该是Item表中的主键,其类型也应该是“可用”?

这个使用检查约束的堆栈溢出答案似乎很接近,但这是唯一的方法吗?我觉得这对于 RDBMS 来说是一件小事,或者这不是规范化的数据?

其他架构设计(我不喜欢)是有一个表称为“菜单”,这可能具有唯一项目可用。问题是这张桌子本质上是非常动态的,它会根据物品的可用性不断变化。而且,我只是根据项目的状态从项目中创建一个子集表,这似乎不是一个好主意。

以编程方式执行此操作很容易;但是,我如何在 RDBMS 中实现这一点?我喜欢数据库足够智能来处理这个的想法。

Mat*_*jaž 3

原来的答案是错误的!请参阅编辑 1以获取更正后的版本。


原答案

一个令人惊奇的解决方案需要视图或继承表中的列的外键,但不幸的是 PostgreSQL(我认为这是您的 RDBMS,因为标签)还没有这个。

我认为对组织数据的方式进行简单的更改就足够了:创建一个像ItemsAvailableQuantity这样的表,将项目与其可用性连接起来,这将在订单中引用。当某件物品不再可用时,DELETE就会从该物品中取出。

CREATE TABLE Item (
    id   serial  NOT NULL
  , name text    NOT NULL
  , cost numeric

  , PRIMARY KEY (id)
  , CONSTRAINT positive_cost
        CHECK (cost > 0)
    );

CREATE TABLE ItemAvailableQuantity (
    id       serial  NOT NULL
  , item_id  integer NOT NULL
  , quantity integer NOT NULL

  , PRIMARY KEY (id)
  , FOREIGN KEY (item_id)
        REFERENCES Item (id)
        ON UPDATE CASCADE
        ON DELETE CASCADE
  , CONSTRAINT postive_quantity -- This constraint is the same as
        CHECK (quantity > 0)    -- checking something like `available = TRUE`.
    );

CREATE TABLE ItemOrder (       -- Changed the name from `Order` because
    id      serial  NOT NULL   -- PostgreSQL refuses that name, somehow
  , bill_id integer NOT NULL
  , item_id integer NOT NULL
  , units   integer NOT NULL

  , PRIMARY KEY (id)
  , FOREIGN KEY (item_id)
        REFERENCES ItemAvailableQuantity (id)
        ON UPDATE CASCADE
        ON DELETE CASCADE
-- Uncomment when `Bill` table is ready
--  , FOREIGN KEY (bill_id)
--        REFERENCES Bill (id)
--        ON UPDATE CASCADE
--        ON DELETE CASCADE
  , CONSTRAINT positive_units
        CHECK (units > 0)
    );
Run Code Online (Sandbox Code Playgroud)

注意!当您的软件减少单位并达到 0 时,约束Positive_units可能会导致问题。如果需要,请使其类似CHECK >= 0,或者添加一个触发器,DELETE当每个或上的单位达到 0(或更少)时自动 -s 行。这将使表ItemAvailableQuantity保留为仅包含实际可用的项目,这就是我们希望从表ItemOrder引用的内容。INSERTUPDATE

这应该可以解决你的问题。这不是你问题的准确答案。这将涉及触发器或CHECK调用函数,如您提供的链接中所示。

要轻松查看商品的数量,只需创建一个连接ItemAvailableQuantityItem 的视图。如果您确实想要,请使用触发器INSERT使其成为可能(请参阅黄框警告) 。


编辑1

实际上Order(又名ItemOrder)应该引用Item而不是ItemAvailableQuantity,以避免当Item当前不可用时出现任何问题,如评论中所述。

这建议我们应该删除整个表ItemAvailableQuantity并仅在Item上添加一列available_quantity

CREATE TABLE Item (
    id                 serial NOT NULL
  , name               text   NOT NULL
  , cost               numeric
  , available_quantity integer NOT NULL

  , PRIMARY KEY (id)
  , CONSTRAINT positive_cost
        CHECK (cost > 0)
  , CONSTRAINT non_negative_quantity
        CHECK (quantity >= 0)
    );
Run Code Online (Sandbox Code Playgroud)

然后,为了确保仅将可用商品插入订单中,我们可以运行

INSERT INTO ItemOrder (bill_id, item_id, units) VALUES
    (SELECT id FROM Bill WHERE condition = something -- customize at will
    , SELECT id FROM Item WHERE available_quantity >= wanted_quantity
        AND other_condition = something
    , wanted_quantity)
    ;
Run Code Online (Sandbox Code Playgroud)

其中wanted_quantity是软件传递给查询的参数。

仍然解决了问题,但并不是问题的直接答案。