为什么我的唯一索引在 Postgresql 中不起作用?

ics*_*kal 3 postgresql postgresql-15

我创建了一个表来存储用户的休息日数据。之后,为多列设置唯一索引,但是当执行插入sql时,它再次重复数据

这是我的 DDL:

create table day_off_movements
(
    id                 bigserial
        primary key,
    user_id            bigint
        constraint fk_day_off_movements_user
            references users,
    proxy_user_id      bigint
        constraint fk_day_off_movements_proxy_users
            references users,
    reason             text,
    day_off_start_date timestamp with time zone,
    day_off_end_date   timestamp with time zone,
    used_days          bigint,
    created_at         timestamp with time zone,
    updated_at         timestamp with time zone
);
Run Code Online (Sandbox Code Playgroud)

指数:

create unique index idx_day_off_all
    on day_off_movements (user_id, proxy_user_id, day_off_start_date, day_off_end_date);
Run Code Online (Sandbox Code Playgroud)

控制查询:

SELECT user_id,proxy_user_id,day_off_end_date,day_off_start_date,count(*)
                 FROM public.day_off_movements t
GROUP BY user_id, proxy_user_id, day_off_end_date, day_off_start_date
Run Code Online (Sandbox Code Playgroud)

输出为 json:

[
  {
    "user_id": 961,
    "proxy_user_id": null,
    "day_off_end_date": "2020-07-29 00:00:00.000000 +00:00",
    "day_off_start_date": "2020-07-27 00:00:00.000000 +00:00",
    "count": 3
  }
]
Run Code Online (Sandbox Code Playgroud)

我有什么错吗?感谢您的帮助。

Yur*_*nko 5

您似乎已在 、 、 和 列上创建user_idproxy_user_id唯一day_off_start_date索引day_off_end_date。这意味着这列的组合在您的表中应该是唯一的。但是,在您的 JSON 中, 的值为proxy_user_id

在 SQL 中,null不被视为值,并且它不像其他值那样参与唯一性约束。这意味着字段中包含nullproxy_user_id的多行不会违反唯一索引,即使其他字段(user_idday_off_start_dateday_off_end_date)相同。

如果您想防止这种情况发生,您有以下几种选择:

  • proxy_user_id不允许字段中出现空值
  • 在 JSON 中使用占位符值而不是null值
  • 使用部分索引

以下是如何创建部分索引的示例:

CREATE UNIQUE INDEX idx_day_off_all
ON day_off_movements (
    user_id, 
    proxy_user_id, 
    day_off_start_date,
    day_off_end_date
)
WHERE proxy_user_id IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)

这将对所有不为空的行强制执行唯一性约束proxy_user_id,但允许多个行具有相同的user_idday_off_start_dateday_off_end_dateifproxy_user_id为空。

  • v15 中新增的另一个选项是将“NULLS NOT DISTINCT”放在索引上。 (2认同)

Boh*_*ian 5

空值不被视为等于任何其他值,包括其他空值(甚至其本身!)。这并不是 Postgres 特有的:它是 SQL 标准。