add batch remove batch split batch comment selection show hidden batches hide batch highlight batch
db<>fiddle
donate feedback about
By using db<>fiddle, you agree to license everything you submit by Creative Commons CC0.
Help with an interesting Postgres question: Why isn't an Index Only Scan used on a partition accessed via the parent table?.
CREATE TABLE users (
users_id serial PRIMARY KEY
, employee_nr int
, is_employee bool NOT NULL DEFAULT false
, CONSTRAINT role_employee CHECK (employee_nr IS NOT NULL = is_employee)
, UNIQUE (is_employee, users_id) -- required for FK (otherwise redundant)
);

CREATE TABLE user_roles (
user_roles_id serial PRIMARY KEY
, users_id int NOT NULL REFERENCES users
, role_name text NOT NULL
, is_employee bool CHECK(is_employee)
, CONSTRAINT role_employee CHECK (role_name <> 'employee' OR is_employee IS TRUE)
, CONSTRAINT role_employee_requires_employee_nr_fk
FOREIGN KEY (is_employee, users_id) REFERENCES users(is_employee, users_id)
);
CREATE TABLE
CREATE TABLE
-- Both triggers are optional convenience features!
-- users
CREATE OR REPLACE FUNCTION trg_users_insup_bef()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
NEW.is_employee := (NEW.employee_nr IS NOT NULL);
RETURN NEW;
END
$func$;

CREATE TRIGGER insup_bef
BEFORE INSERT OR UPDATE OF employee_nr ON users
FOR EACH ROW
EXECUTE FUNCTION trg_users_insup_bef();


-- user_roles
CREATE OR REPLACE FUNCTION trg_user_roles_insup_bef()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
NEW.is_employee = true;
RETURN NEW;
END
$func$;

CREATE TRIGGER insup_bef
BEFORE INSERT OR UPDATE OF role_name ON user_roles
FOR EACH ROW
WHEN (NEW.role_name = 'employee')
EXECUTE FUNCTION trg_user_roles_insup_bef();
CREATE FUNCTION
CREATE TRIGGER
CREATE FUNCTION
CREATE TRIGGER
-- sample data
INSERT INTO users(users_id, employee_nr) VALUES
(default, NULL)
, (default, NULL)
, (default, 123) -- this one
RETURNING *;

INSERT INTO user_roles(users_id, role_name) VALUES
(1, 'killer')
, (2, 'burgler')
, (3, 'thief')
, (3, 'employee')
RETURNING *;
users_id employee_nr is_employee
1 null f
2 null f
3 123 t
INSERT 0 3
user_roles_id users_id role_name is_employee
1 1 killer null
2 2 burgler null
3 3 thief null
4 3 employee t
INSERT 0 4
-- Violates FK and fails!
INSERT INTO user_roles(users_id, role_name) VALUES (2, 'employee');
UPDATE users SET employee_nr = NULL WHERE users_id = 3;
ERROR:  insert or update on table "user_roles" violates foreign key constraint "role_employee_requires_employee_nr_fk"
DETAIL:  Key (is_employee, users_id)=(t, 2) is not present in table "users".
DELETE FROM user_roles WHERE user_roles_id = 4;
-- now we can:
UPDATE users SET employee_nr = NULL WHERE users_id = 3;

UPDATE users SET employee_nr = 436 WHERE users_id = 2;
-- now it works:
INSERT INTO user_roles(users_id, role_name) VALUES (2, 'employee');
DELETE 1
UPDATE 1
UPDATE 1
INSERT 0 1
TABLE users;
TABLE user_roles;
users_id employee_nr is_employee
1 null f
3 null f
2 436 t
SELECT 3
user_roles_id users_id role_name is_employee
1 1 killer null
2 2 burgler null
3 3 thief null
6 2 employee t
SELECT 4
-- cannot take that back now:
UPDATE users SET employee_nr = NULL WHERE users_id = 2;
ERROR:  update or delete on table "users" violates foreign key constraint "role_employee_requires_employee_nr_fk" on table "user_roles"
DETAIL:  Key (is_employee, users_id)=(t, 2) is still referenced from table "user_roles".