英文:
How to enforce "at least one" in a one-to-many relationship?
问题
[![ERD][1]][1]
如何在PostgreSQL中实现这种关系?
我需要确保每个会议至少有一个会话,但我不知道如何实现它。
这是我的尝试:
```sql
CREATE TABLE Meeting(
MeetingId INT PRIMARY KEY NOT NULL,
Cost INT NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
);
CREATE TABLE Session(
SessId INT PRIMARY KEY NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
MeetingId INT NOT NULL REFERENCES Meeting(MeetingId) ON UPDATE CASCADE ON DELETE CASCADE
);
我应该添加一些新表或约束吗?
<details>
<summary>英文:</summary>
[![ERD][1]][1]
How to implement this relationship in PostgreSQL?
I need every meeting to have at least one session, but I do not how to realize it.
This is my try:
```sql
CREATE TABLE Meeting(
MeetingId INT PRIMARY KEY NOT NULL,
Cost INT NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
);
CREATE TABLE Session(
SessId INT PRIMARY KEY NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
MeetingId INT NOT NULL REFERENCES Meeting(MeetingId) ON UPDATE CASCADE ON DELETE CASCADE
);
Should I add some new tables or constraints?
答案1
得分: 3
以下是翻译好的内容:
这似乎是一个鸡生蛋的问题,但有一个(大部分)简单的解决方案,可以完美解决:
CREATE TABLE meeting (
meeting_id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
, first_session_id int NOT NULL
, cost int NOT NULL
, startdate date NOT NULL
, enddate date NOT NULL
);
CREATE TABLE session (
session_id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
, meeting_id int NOT NULL
, startdate date NOT NULL
, enddate date NOT NULL
, CONSTRAINT session_meeting_id_fkey
FOREIGN KEY (meeting_id) REFERENCES meeting ON UPDATE CASCADE ON DELETE CASCADE
, UNIQUE (meeting_id, session_id) -- needed for FK meeting_first_session_id_fkey
);
ALTER TABLE meeting ADD CONSTRAINT meeting_first_session_id_fkey
FOREIGN KEY (meeting_id, first_session_id) REFERENCES session(meeting_id, session_id); -- no cascading
现在,不可以插入会议而不插入会话。并且一直强制执行引用完整性。为了避免另一个潜在的鸡生蛋问题,使用数据修改的 CTE 来插入会议 和 其第一个会话:
WITH ins_meeting AS (
INSERT INTO meeting
(first_session_id , cost, startdate , enddate)
VALUES (nextval(pg_get_serial_sequence('session', 'session_id')), 1 , '2023-03-08', '2023-03-09')
RETURNING meeting_id, first_session_id
)
INSERT INTO session (session_id, meeting_id, startdate, enddate)
SELECT first_session_id, meeting_id, '2023-03-08', '2023-03-09'
FROM ins_meeting;
这是基于以前的答案进行优化的:
- https://stackoverflow.com/questions/24813000/how-to-deal-with-mutually-dependent-inserts/24816197#24816197
- https://stackoverflow.com/questions/68244745/chicken-or-egg-problem-in-table-schema-postgresql/68245259#68245259
关于演示的 IDENTITY
列:
请注意使用 nextval(pg_get_serial_sequence('session', 'session_id'))
。请参考:
PRIMARY KEY
列在隐式情况下是 NOT NULL
。拼写出来是可选的噪音。请参考:
- https://stackoverflow.com/questions/20006374/why-can-i-create-a-table-with-primary-key-on-a-nullable-column/20006502#20006502
- https://stackoverflow.com/questions/26642835/composite-primary-key-enforces-not-null-constraints-on-involved-columns/26643137#26643137
英文:
This may seem like a chicken-egg problem, but there is a (mostly) simple solution that works perfectly:
CREATE TABLE meeting (
meeting_id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
, first_session_id int NOT NULL
, cost int NOT NULL
, startdate date NOT NULL
, enddate date NOT NULL
);
CREATE TABLE session (
session_id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
, meeting_id int NOT NULL
, startdate date NOT NULL
, enddate date NOT NULL
, CONSTRAINT session_meeting_id_fkey
FOREIGN KEY (meeting_id) REFERENCES meeting ON UPDATE CASCADE ON DELETE CASCADE
, UNIQUE (meeting_id, session_id) -- needed for FK meeting_first_session_id_fkey
);
ALTER TABLE meeting ADD CONSTRAINT meeting_first_session_id_fkey
FOREIGN KEY (meeting_id, first_session_id) REFERENCES session(meeting_id, session_id); -- no cascading
Now, a meeting cannot be inserted without also inserting a session. And referential integrity is enforced at all times.
To avoid another lurking chicken-egg problem, use a data-modifying CTE to insert a meeting and its first session:
WITH ins_meeting AS (
INSERT INTO meeting
(first_session_id , cost, startdate , enddate)
VALUES (nextval(pg_get_serial_sequence('session', 'session_id')), 1 , '2023-03-08', '2023-03-09')
RETURNING meeting_id, first_session_id
)
INSERT INTO session (session_id, meeting_id, startdate, enddate)
SELECT first_session_id, meeting_id, '2023-03-08', '2023-03-09'
FROM ins_meeting;
This is optimized based on previous answers:
- https://stackoverflow.com/questions/24813000/how-to-deal-with-mutually-dependent-inserts/24816197#24816197
- https://stackoverflow.com/questions/68244745/chicken-or-egg-problem-in-table-schema-postgresql/68245259#68245259
About the demonstrated IDENTITY
columns:
Note the use of nextval(pg_get_serial_sequence('session', 'session_id'))
. See:
A PRIMARY KEY
column is NOT NULL
implicitly. Spelling that out is optional noise. See:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论