kjo*_*kjo 6 postgresql sqlite database-design constraint
我有一个x这样定义的表:
CREATE TABLE x (
xid INTEGER NOT NULL PRIMARY KEY,
yid INTEGER NOT NULL REFERENCES y(yid),
is_principal BOOLEAN NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
该定义遗漏了一个必须满足的约束x。用英语来说,这个约束可以这样描述:
字段中可能有一行或多行具有给定值
yid,但其中必须始终有一行的is_principal字段为TRUE1。
我正在寻找一种方法来强制执行此约束。
(如果重要的话,我对适用于 SQLite3 和 PostgreSQL 的解决方案特别感兴趣。)
编辑:为了清楚起见,上面的描述并不排除 table 中存在y其值yid在 table 中根本没有提及的行x。对于 的此类值,yid根本没有任何价值xid,无论是本金价值还是其他价值。仅对于yid 表中出现的 x那些值,表中必须有且只有一行x具有is_principal = TRUE。
1表达相同约束的另一种方法是以下两个查询应始终产生相同的输出:
SELECT DISTINCT yid FROM x ORDER BY yid;
SELECT yid FROM x WHERE is_principal ORDER BY yid;
Run Code Online (Sandbox Code Playgroud)
问题与此类似:如何与特权孩子建立一对多关系?
约束的“每组最多一个”部分可以通过部分索引来解决:
CREATE UNIQUE INDEX is_FavoriteChild
ON x (yid)
WHERE is_principal ;
Run Code Online (Sandbox Code Playgroud)
解决问题的另一种方法是删除该is_principal列并添加第三个表。这也不能解决“完全一样”的问题:
CREATE TABLE x (
xid INTEGER NOT NULL PRIMARY KEY,
yid INTEGER NOT NULL REFERENCES y (yid),
UNIQUE (yid, xid)
);
CREATE TABLE x_principal (
xid INTEGER NOT NULL,
yid INTEGER NOT NULL PRIMARY KEY,
FOREIGN KEY (yid, xid) REFERENCES x (yid, xid)
);
Run Code Online (Sandbox Code Playgroud)
如果您想单独使用 DDL 强制执行“恰好一个”限制,可以在具有可延迟约束的 Postgres 中完成(我不认为这是 SQLite 中的一个选项)。
有关更多详细信息和选项,您可以在@Erwin的SO问题中看到出色的答案:SQLAlchemy中的复杂外键约束。
(编辑答案以获取附加详细信息,即并非 的所有值都y.yid必须出现在 表 中x。添加了一个表):
--- This table will hold all values of yid that appear in table x
CREATE TABLE y_x (
yid INTEGER NOT NULL PRIMARY KEY REFERENCES y (yid),
--- **no other columns**
principal_xid INTEGER NOT NULL
);
CREATE TABLE x (
xid INTEGER NOT NULL PRIMARY KEY,
yid INTEGER NOT NULL REFERENCES y_x (yid)
DEFERRABLE INITIALLY DEFERRED,
UNIQUE (yid, xid)
);
ALTER TABLE y_x
ADD CONSTRAINT y_principal_x_fk
FOREIGN KEY (yid, principal_xid)
REFERENCES x (yid, xid)
DEFERRABLE INITIALLY DEFERRED;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3910 次 |
| 最近记录: |