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;
fiddle
This is optimized based on previous answers:
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: