Sql 具有非零基数的多对多关系

Sql 具有非零基数的多对多关系,sql,database,postgresql,database-design,constraints,Sql,Database,Postgresql,Database Design,Constraints,假设我有一个带有表a、B和C的PostgreSQL数据库,其中a和B通过连接表C具有多对多关系。这些表具有以下SQL定义: CREATE TABLE A ( id serial NOT NULL, CONSTRAINT A_pkey PRIMARY KEY (id) ) CREATE TABLE B ( id serial NOT NULL, CONSTRAINT B_pkey PRIMARY KEY (id) ) CREATE TABLE C ( A_id integ

假设我有一个带有表a、B和C的PostgreSQL数据库,其中a和B通过连接表C具有多对多关系。这些表具有以下SQL定义:

CREATE TABLE A
( 
  id serial NOT NULL,
  CONSTRAINT A_pkey PRIMARY KEY (id)
)

CREATE TABLE B
(
  id serial NOT NULL,
  CONSTRAINT B_pkey PRIMARY KEY (id)
)

CREATE TABLE C
(
  A_id integer NOT NULL,
  B_id integer NOT NULL,
  CONSTRAINT C_pk PRIMARY KEY (A_id, B_id),
  CONSTRAINT A_fk FOREIGN KEY (A_id)
      REFERENCES A(id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT B_fk FOREIGN KEY (B_id)
      REFERENCES B(id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

现在我想确保每个A实例至少有一个B实例(换句话说:A中的每个id作为C中的A_id至少出现一次)。是否可以使用数据库约束检查此属性?

更好的方法是约束触发器

它应该对表C执行exists()检查,并在所需键不存在时引发异常。(从技术上讲,强制外键约束的内置触发器基本上就是这样做的。)

它会是这样的:

create function A_C_fkey_check() returns trigger as $$
begin
  if not exists (select 1 from C where A_id = new.id) then
    raise exception 'invalid key';
  end if;
  return null;
end;
$$ language plpgsql;

create constraint trigger A_C_fkey_check after insert on A
for each row execute procedure A_C_fkey_check();

也就是说,我认为在数据库级别强制执行这种类型的约束并不是一个好主意:在我自己的经验中,需要a而不需要B的奇怪边缘情况总是会出现。

您是否尝试过创建一个从
a.id
C.a\u id
的不同外键?是的,我考虑过了。但是外键应该唯一地标识另一个表中的行。在这种情况下,通常会有多个C.A_id与A.id值匹配,因此它不是唯一的。我不确定这种方法是否仍然有效,但对我来说这听起来有点脏;它允许您对C表中的引用数量设置任何限制。但它需要C表上的触发器函数来维护A表和B表中的计数。顺便说一句,我认为没有触发器的解决方案是不可能的。在{A,B}表中有孤立行是一个很大的问题吗?有逻辑上的理由禁止它们吗?(对我来说,没有工作者的项目,没有项目的工作者似乎很好,至少暂时是这样。比发明虚拟C行来捕获空闲行要好)在大多数SQL DBMS中,这种类型的“至少N”包含依赖几乎不可能在声明性代码中维护。这或多或少是SQL模型的一个限制,即一次只能更新一个表。常见的解决方法是在过程代码(例如触发器)中强制执行规则、在外部规则引擎中强制执行规则或干脆不强制执行规则。要添加到@sqlvogel优秀注释,您还可以创建一个后台作业(可能通过存储过程),查找违反规则的情况,并在出现错误时通知您。这不如实时管理好,但它会提醒您代码更改会无意中导致此问题。。