sql - 使用外键,可以引用列和固定值吗?

标签 sql database postgresql database-design foreign-keys

以下是我的用例:

我有 4 个表:

CREATE TABLE A
(
  name character(20) NOT NULL,
  id integer NOT NULL,
  CONSTRAINT a_pkey PRIMARY KEY (id)
)

CREATE TABLE B
(
  name character(20) NOT NULL,
  id integer NOT NULL,
  CONSTRAINT b_pkey PRIMARY KEY (id)
)

CREATE TABLE C
(
  name character(20) NOT NULL,
  id integer NOT NULL,
  CONSTRAINT c_pkey PRIMARY KEY (id)
)

CREATE TABLE X
(
  type character(20) NOT NULL,
  other_id integer NOT NULL,
  id integer NOT NULL,
  CONSTRAINT "X_PK" PRIMARY KEY (id)
)

表 X 中的“other_id”可以是表 A、B 或 C 中任何一个的“id”。表 X 中的“类型”列应该表示表 A、B 或 C 中的哪一个“id”存储在“other_id”中

来自表 x 的示例数据:

type  other_id  id
"A"    1         1
"B"    1         2
"C"    1         3
"A"    2         4

尝试使用固定值在表 X-A、X-B 和 X-C 之间创建复合 FK,如下所示

ALTER TABLE x
ADD CONSTRAINT X_A_FK FOREIGN KEY (other_id, type) REFERENCES a (id, 'A') ON DELETE CASCADE

但我收到此错误(所有 FK 的问题相同):

ERROR: syntax error at or near "'A'"

我的问题是,使用外部组合键,可以引用列和固定值吗?如果不是,解决这个问题的更好方法是什么?

最佳答案

我建议另一种设计:

CREATE TABLE a (
  a_id integer PRIMARY KEY -- pk is NOT NULL automatically
 ,a    text NOT NULL
);

CREATE TABLE b ( ...);
CREATE TABLE c ( ...);

CREATE TABLE x (
  x_id integer PRIMARY KEY        -- or maybe a serial?
 ,a_id integer REFERENCES a(a_id) -- can be NULL
 ,b_id integer REFERENCES b(b_id)
 ,c_id integer REFERENCES c(c_id)
);

重点是在您的设计中使用三列(a_id、b_id、c_id)而不是两列(other_id、type)。与人们的想法相反,这需要更少 磁盘空间,同时更干净、更简单。您不需要额外的约束或索引来强制执行参照完整性。
NULL storage is cheap.

如果你想强制执行,那最多的(a, b, c) 一次可以链接,添加一个CHECK constraint :

ALTER TABLE x ADD CONSTRAINT x_max1_fk
CHECK (a_id IS NULL AND b_id IS NULL
    OR b_id IS NULL AND c_id IS NULL
    OR a_id IS NULL AND c_id IS NULL)

看似冗长,但非常廉价和简单。

如果您想要强制执行一次必须链接exactly one (a, b, c),请修改为:

ALTER TABLE x ADD CONSTRAINT x_exactly1_fk
CHECK (a_id IS NULL AND b_id IS NULL AND c_id IS NOT NULL
    OR b_id IS NULL AND c_id IS NULL AND a_id IS NOT NULL
    OR a_id IS NULL AND c_id IS NULL AND b_id IS NOT NULL)

对于不仅仅是几列,我会使用:

ALTER TABLE x ADD CONSTRAINT x_exactly1_fk
CHECK ((a_id IS NULL)::int
     + (b_id IS NULL)::int
     + (c_id IS NULL)::int = 1)  -- or <= 1 for the former case

请注意我是如何避免列名 idname 的。请改用描述性名称,这会让您在加入几个表时更轻松 - 您将必须对这样的设计做很多事情,而这正是您在关系数据库中的事情。 p>

我使用 text 而不是 varchar(20)Why?

我也会考虑 ON UPDATE CASCADE并且可能是外键的 ON DELETE CASCADE 修饰符。

关于sql - 使用外键,可以引用列和固定值吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20504319/

相关文章:

sql - psql:尽管存在关系,但未找到任何关系

sql - 如何删除不需要的行值,然后根据 ADF/Azure SQL DB 中的键合并行?

没有最小值和最大值的 SQL 聚合

database - Azure 认知搜索能否用作某些数据的主数据库?

java - aws 凭证在 Android 示例应用程序中不起作用

postgresql - postgres 服务器中的未知文件

java - Hibernate 正确的级联映射

sql - SELECT TOP N 与变量

sql - 为什么这个 MySQL Create Table 语句会失败?

c# - 使用 ADO.NET 创建新数据库 (.mdb)