I have a users
table in my Postgres database with a role
column that uses an enum. The user can either be a CAPTAIN
or PLAYER
.
DROP TYPE IF EXISTS USER_ROLE CASCADE;
CREATE TYPE USER_ROLE AS ENUM ('CAPTAIN', 'PLAYER');
CREATE TABLE IF NOT EXISTS "user" {
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
role USER_ROLE NOT NULL
);
I need to create a junction table, connecting many CAPTAIN
users with many PLAYER
users.
I can create a table like this:
CREATE TABLE IF NOT EXISTS user_user (
user_captain_id INT,
user_player_id INT,
CONSTRAINT pk_user_user PRIMARY KEY (user_captain_id, user_player_id),
CONSTRAINT fk_user_captain FOREIGN KEY (user_captain_id) REFERENCES "user"(id),
CONSTRAINT fk_user_player FOREIGN KEY (user_player_id) REFERENCES "user"(id),
);
Is there any way to add a constraint so that user_captain_id
has to be a user who has the CAPTAIN
role and user_player_id
has to be a user who has the PLAYER
role?
2
Answers
Since
PostgreSQL
doesn’t support direct table references in check constraints, I used a combination of a custom function (check_user_role
) and triggers to ensure data integrity. Thecheck_user_role
function checks if a given user ID corresponds to the specified role (CAPTAIN
orPLAYER
). Then, two triggers (trigger_check_captain_role
andtrigger_check_player_role
) are created on the user_user table. These triggers call the function before any insert or update operation to validate thatuser_captain_id
is always associated with aCAPTAIN
anduser_player_id
with aPLAYER
. This approach will make sure that the junction table accurately reflects the relationships between users and their respective roles, maintaining the integrity and correctness of the role associations in your database.And then write a Trigger
Here is a bullet-proof implementation with multicolumn FK constraints:
fiddle
Downsides:
Redundant
UNIQUE
constraintusers_id_role_uniq
in tableusers
.Redundant columns
captain_role
andplayer_role
in tableuser_user
. Since those hold constant values, I made both out to be generated columns. So you don’t have to worry about them, and you also cannot corrupt them. (Sadly, FK constraints do not allow constants instead of actual columns.)About generated columns:
Related:
Aside: I wouldn’t create an
enum
for allowed roles, and just use a 1-byte"char"
field with aCHECK
constraint. Simpler, smaller, faster, and easier to adapt later on. See:fiddle