当 PK 复合时,如何强制父母必须至少有一个孩子?

ali*_*noi 4 postgresql database-design constraint

考虑以下业务领域:

  • AnAirline具有唯一的航空公司 ID ( aid) 并包含零个或多个Planes
  • A在其飞行目的地中Plane有一个唯一的飞机 id ( pid) Airline(但来自不同国家的飞机Airlines可以有重叠ids)。
  • 每个Plane都有一个或多个Seats
  • A在其平面上Seat有一个唯一的座位 id ( sid) (但Seats不同的座位 idPlanes可能有重叠ids)。

到目前为止我的尝试

这是我解决这个问题的尝试:

CREATE SEQUENCE planes_seq;

CREATE TABLE Airlines (
    aid INTEGER PRIMARY KEY DEFAULT nextval('planes_seq')
);

CREATE TABLE Planes (
    aid INTEGER REFERENCES Airlines(aid)
,   pid INTEGER
,   PRIMARY KEY(aid, pid)
);

CREATE TABLE Seats (
    aid INTEGER
,   pid INTEGER
,   sid INTEGER
,   PRIMARY KEY(aid, pid, sid)
,   FOREIGN KEY(aid, pid) REFERENCES Planes(aid, pid)
);

ALTER TABLE Planes ADD CONSTRAINT fk_seats FOREIGN KEY(aid, pid)
REFERENCES Seats(aid, pid);
Run Code Online (Sandbox Code Playgroud)

然而,它失败了,因为决赛ALTER TABLE是非法的,因为这对(Seats.aid, Seats.pid)确实不是唯一的。

问题

  • 我如何强制执行“一架飞机至少有一个座位”的限制?
  • 这个方案是第三范式吗?

PS:我是 SQL 新手,这是家庭作业的(一小部分)。我尝试遵循另一个 DBA 问题中的父级至少一个子级示例(约束在数据库中强制执行“至少一个”或“恰好一个”),但是那里有太多我无法做到的技巧理解(多个WITH查询、一个RETURNING子句等)。在我看来,必须有一种更简单的方法来做到这一点。

ype*_*eᵀᴹ 5

CTE (WITHRETURNING) 的存在是为了避免使用延迟约束。

从概念上讲,使用延迟约束更简单,对于家庭作业,我建议您首先使用它们。例如,与您的注释不同的地方:

CREATE SEQUENCE planes_seq;

CREATE TABLE airlines (
    aid INTEGER PRIMARY KEY DEFAULT nextval('planes_seq')
);

CREATE TABLE planes (
    aid INTEGER NOT NULL REFERENCES airlines (aid)
,   pid INTEGER NOT NULL 
,   sid INTEGER NOT NULL                -- we add a default seat
        DEFAULT 1 CHECK (sid = 1)       -- in every plane
,   PRIMARY KEY (aid, pid)
);

CREATE TABLE seats (
    aid INTEGER NOT NULL
,   pid INTEGER NOT NULL
,   sid INTEGER NOT NULL
,   PRIMARY KEY (aid, pid, sid)
,   FOREIGN KEY (aid, pid) REFERENCES planes (aid, pid) 
);

ALTER TABLE planes ADD CONSTRAINT fk_seats 
    FOREIGN KEY (aid, pid, sid) 
    REFERENCES seats (aid, pid, sid) 
    DEFERRABLE INITIALLY DEFERRED ;      -- the DEFERRABLE is important
Run Code Online (Sandbox Code Playgroud)

现在我们可以添加一些数据:

INSERT INTO airlines (aid) 
VALUES (1) ;                      -- our 1st airline

BEGIN ;
    INSERT INTO  planes      -- its 1st plane
        (aid, pid)           -- notice: sid is added by default as 1         
    VALUES 
        (1, 1) ;

    INSERT INTO seats 
        (aid, pid, sid) 
    VALUES                    -- 3 seats in the 1st plane
        (1, 1, 1), 
        (1, 1, 2), 
        (1, 1, 3) ;
COMMIT ;
Run Code Online (Sandbox Code Playgroud)

我们需要在事务中插入新飞机(及其座位),否则会失败。

DEFERRABLE意味着我们可以“推迟”对FOREIGN KEY约束的检查,直到事务结束(在插入/更新/删除语句之后立即检查不是推迟的约束。)如果您尝试仅在平面中插入,它将失败。

INITIALLY DEFERRED意味着我们希望在外键约束创建后立即“推迟”它。这本来可以稍后完成(您可以设置和取消设置延迟约束,如果它们已定义为DEFERRABLE)。