具有空值的 sqlite3 复合唯一约束

sqlite3 composite unique contraint with null values

如何设置可以正确处理 sqlite 中的空值的复合唯一约束?这可能更好地用例子来表达:

CREATE TABLE people (
    id INTEGER PRIMARY KEY,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL,
    suffix TEXT,
    UNIQUE (first_name, last_name, suffix)
);

当多次输入带有后缀的人时,约束会按预期工作。

-- Last insert is denied
INSERT INTO people (first_name, last_name, suffix)
VALUES
    ('Joe', 'Dirt', 'Sr'),
    ('Joe', 'Dirt', 'Jr'),
    ('Joe', 'Dirt', 'Sr');

但是,当一个没有后缀的人被多次插入时,唯一约束是不适应的。

-- Both are permitted
INSERT INTO people (first_name, last_name)
VALUES
    ('Chris', 'Farley'),
    ('Chris', 'Farley');

我知道 sqlite 将 null 值视为相互独立的,但是有没有正确的方法来解决这个问题?

如有任何帮助,我们将不胜感激。谢谢!

只有当后缀为 null 时,您才能为 first_namelast_name 创建唯一的 partial index:

CREATE UNIQUE INDEX idx_people ON people(first_name, last_name) 
WHERE suffix IS NULL;

查看简化版 demo.

如果你的 SQLite 版本是 3.31.0+,你可以创建一个生成的列,当 suffixnull 时,returns 一个空字符串,并在唯一约束中使用它而不是 suffix:

CREATE TABLE people (
    id INTEGER PRIMARY KEY,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL,
    suffix TEXT,
    str_suffix GENERATED ALWAYS AS (COALESCE(suffix, '')),
    UNIQUE (first_name, last_name, str_suffix)
);

将后缀空字符串设为默认值并将其设置为不为空。这可能会解决您的问题(NULL 与 '' 不同,NULL 是我不知道的,所以对于下面的代码,您说的是中间名是空字符串,而不是 NULL)。

CREATE TABLE people (
    id INTEGER PRIMARY KEY,
    first_name TEXT NOT NULL,
    last_name TEXT NOT NULL,
    suffix TEXT DEFAULT '' NOT NULL,
    UNIQUE (first_name, last_name, suffix)
);