刚才有人在Twitter上问我一个非常有趣的问题:
@lukaseder快速问:在PG中,我可以有一个复合外键,其中一个值是常数......还是我必须在表中存储常数? constraint foreign key (foo_id, 'bar_subtype') references foo(foo_id,foo_type) ?
- 看!皇帝没穿衣服 V⃝(@connolly_s)2020年9月10日
我们可以在(PostgreSQL)表中设置 "常量 "外键列吗?幸运的是,是的,我们可以。使用一个很好的标准功能,即"计算列 "或 "生成列"有时,无论出于什么原因,你不能完全规范化你的模式。可能有这样一种情况:你有一个带有复合主键的表,像这样:
CREATE TABLE t1 (
a int,
b int,
t1 int,
PRIMARY KEY (a, b)
)
当然,你可以创建一个带有CHECK约束的表t2,确保b=1:
CREATE TABLE t2 (
a int,
b int NOT NULL DEFAULT 1 CHECK (b = 1),
t2 int,
FOREIGN KEY (a, b) REFERENCES t1
)
但为什么不使用一个生成的列来代替呢?
CREATE TABLE t2 (
a int,
b int GENERATED ALWAYS AS (1) STORED,
t2 int,
FOREIGN KEY (a, b) REFERENCES t1
)
在我看来,这甚至更强大。从PostgreSQL 12开始,只支持STORED(意味着数值存储在磁盘上),而在这种情况下,VIRTUAL会更好(意味着数值只在读取行时产生)。 插入一些测试数据:
INSERT INTO t1 (a, b, t1)
VALUES(1, 1, 1), (1, 2, 2), (2, 1, 3);
INSERT INTO t2 (a, t2)
VALUES (1, 11), (2, 12);
SELECT *
FROM t1
NATURAL LEFT JOIN t2
产生了预期的结果。我们只能将(b=1)插入到t2中:
a|b|t1|t2|
-|-|--|--|
1|1| 1|11|
2|1| 3|12|
1|2| 2| |
一个很好的技巧,可以保留在袖子里。 计算或生成的列在各种RDBMS中可用,至少包括:
- Db2
- MySQL
- Oracle
- PostgreSQL
- SQL服务器