Skip to content
Advertisement

SQL check constraint with multiple conditions

I have a table called timesheets. In that table there are 4 columns for which I want to create a constraint so that only the following combinations are possible:

  • If user_is is set then task_schedule_id must be null.
  • If location_id or customer_id is set, then require user_id to be not null and ensure that not both location_id and customer_id are set.
  • If task_schedule_id is set then require user_id, customer_id and location_id to be null.

This is the table:

CREATE TABLE IF NOT EXISTS timesheets
(
    id               uuid        NOT NULL DEFAULT gen_random_uuid(),
    created_at       timestamptz NOT NULL DEFAULT current_timestamp,
    updated_at       timestamptz NOT NULL DEFAULT current_timestamp,
    deleted_at       timestamptz NULL,

    -- Where and who (check if location_id or customer_id is set then require user)
    location_id      uuid        NULL,
    customer_id      uuid        NULL,
    user_id          uuid        NULL,

    -- Or what... BUT not both
    task_schedule_id uuid        NULL,

    -- Billing
    billable         bool        NOT NULL DEFAULT TRUE,
    billed_at        timestamptz NULL,
    
    -- fks and pk
    FOREIGN KEY (user_id) REFERENCES users (id),
    FOREIGN KEY (task_schedule_id) REFERENCES task_schedules (id),
    FOREIGN KEY (location_id) REFERENCES locations (id),
    FOREIGN KEY (customer_id) REFERENCES customers (id),
    PRIMARY KEY (id)
);

And this is what I have so far:

ALTER TABLE timesheets
    ADD constraint only_one_group
        check (
                ((user_id is null and customer_id is null and location_id is null) and
                 task_schedule_id is not null)
                or
                (user_id is not null and not (customer_id is null and location_id is null) and
                 (customer_id is null or location_id is null) and
                 task_schedule_id is null)
            );

The context is that a task_schedule links to a task which can contain a location_id and or a customer_id. The idea is that timesheets can be created globally or in combination with tasks.

Advertisement

Answer

You can write the constraints as:

ALTER TABLE timesheets
  ADD constraint just_user__or__location_or_customer_with_user__or__just_task check (
      (
        user_id is not null
        and task_schedule_id is null
        and (
          (location_id is null and customer_id is null)
          and (location_id is not null or customer_id is not null)
        )
      ) or (
        (location_id is not null or customer_id is not null)
        and not (location_id is not null and customer_id is not null)
        and user_id is not null
      ) or (
        task_schedule_id is not null
        and user_id is null
        and location_id is null
        and customer_id is null
      )
    );
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement