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 中实现这一点?我喜欢数据库足够智能来处理这个的想法。
原来的答案是错误的!请参阅编辑 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引用的内容。INSERT
UPDATE
这应该可以解决你的问题。这不是你问题的准确答案。这将涉及触发器或CHECK
调用函数,如您提供的链接中所示。
要轻松查看商品的数量,只需创建一个连接ItemAvailableQuantity和Item 的视图。如果您确实想要,请使用触发器INSERT
使其成为可能(请参阅黄框警告) 。
实际上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
是软件传递给查询的参数。
仍然解决了问题,但并不是问题的直接答案。