SQL 检查具有多个条件的约束

SQL check constraint with multiple conditions

我有一个名为时间表的 table。在那个 table 中,我想为其创建一个约束的 4 列,以便只有以下组合是可能的:

这是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)
);

这是我目前所拥有的:

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)
            );

上下文是 task_schedule 链接到可以包含 location_id 和/或 customer_id 的任务。这个想法是时间表可以全局创建或与任务结合创建。

您可以将约束写为:

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
      )
    );