使一行中的所有非空值唯一
Make all non-null values in a row unique
我有一个 table 包含以下列:id、col1、col2、col3、col4、col5、col6。
约束表示至少填充 3 列(因此最多 3 个 NULL)。 (列不按顺序填,所以可以填col1,col2,col5,col3,col4,col6为NULL)
如何确保当该列不为 NULL 时,它在该行的其他列中是唯一的?
如何确保非 NULL 值的组合在所有行中都是唯一的?
我目前添加了以下约束(以确保至少有 3 个非空值):
ALTER TABLE my_table
ADD CONSTRAINT my_constraint CHECK (
(
(CASE WHEN col1 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col2 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col3 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col4 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col5 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col6 IS NULL THEN 0 ELSE 1 END)
) >= 3
)
由于所有 NULL
值,这不是一个有趣的约束,但我认为它做到了;
alter table my_table add constraint chk_ugly check
( (col1 is null or col1 <> all (array_remove(array[col2, col3, col4, col5, col6], null))) and
(col2 is null or col2 <> all (array_remove(array[col1, col3, col4, col5, col6], null))) and
(col3 is null or col3 <> all (array_remove(array[col1, col2, col4, col5, col6], null))) and
(col4 is null or col4 <> all (array_remove(array[col1, col2, col3, col5, col6], null))) and
(col5 is null or col5 <> all (array_remove(array[col1, col2, col3, col4, col6], null))) and
(col6 is null or col6 <> all (array_remove(array[col1, col2, col3, col4, col5], null)))
)
我确实认为有更好的方法来组织数据,每个 col
和 id
一行。那将是一个新的 table。也就是说,很难对强制执行的三个值施加约束。
注意:您现有的约束也可以在 Postgres 中进行简化:
ALTER TABLE my_table
ADD CONSTRAINT my_constraint CHECK (
cardinality(array_remove(array[col2, col3, col4, col5, col6], null)) >= 3
)
据我所知,这些实际上是三个不同的约束。必须存在至少三个非空值的方法很简单:
ALTER TABLE my_table
ADD CONSTRAINT my_constraint CHECK ( num_nonnulls(c1, c2, c3, c4, c5, c6) >= 3);
单行的 6 列中不应存在重复值的要求可以通过创建一个函数来检查:
create or replace function all_unique(p_row my_table)
returns boolean
as
$$
select not exists (
select v
from (
values (p_row.c1), (p_row.c2), (p_row.c3), (p_row.c4), (p_row.c5), (p_row.c6)
) t(v)
where v is not null
group by v
having count(*) > 1);
$$
language sql
immutable
strict;
函数 returns 如果所有非空值在一行中都是唯一的,则为真。我将其定义为接收完整的行,以便在添加或删除新列时更容易适应。我不确定这是否是最有效或最简单的方法,但我现在想不出别的方法。
这可用于检查约束:
ALTER TABLE my_table
ADD CONSTRAINT all_unique CHECK ( all_unique(my_table) );
最后一个要求,非空值在多行中应该是唯一的,可以用唯一索引来实现。但是为此,我们需要一个函数来以排序的方式创建非空值数组:
create or replace function non_null_values(p_row my_table)
returns text[]
as
$$
select array(
select distinct v
from (
values (p_row.c1), (p_row.c2), (p_row.c3), (p_row.c4), (p_row.c5), (p_row.c6)
) t(v)
where v is not null
order by v);
$$
language sql
immutable
strict;
为什么要排序?因为 Postgres 认为数组 [1,2,3]
不等于 (=
) 数组 [3,1,2]
,这是唯一索引使用的。
此函数可用于唯一索引:
create unique index on my_table (non_null_values(my_table));
我有一个 table 包含以下列:id、col1、col2、col3、col4、col5、col6。
约束表示至少填充 3 列(因此最多 3 个 NULL)。 (列不按顺序填,所以可以填col1,col2,col5,col3,col4,col6为NULL)
如何确保当该列不为 NULL 时,它在该行的其他列中是唯一的? 如何确保非 NULL 值的组合在所有行中都是唯一的?
我目前添加了以下约束(以确保至少有 3 个非空值):
ALTER TABLE my_table
ADD CONSTRAINT my_constraint CHECK (
(
(CASE WHEN col1 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col2 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col3 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col4 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col5 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN col6 IS NULL THEN 0 ELSE 1 END)
) >= 3
)
由于所有 NULL
值,这不是一个有趣的约束,但我认为它做到了;
alter table my_table add constraint chk_ugly check
( (col1 is null or col1 <> all (array_remove(array[col2, col3, col4, col5, col6], null))) and
(col2 is null or col2 <> all (array_remove(array[col1, col3, col4, col5, col6], null))) and
(col3 is null or col3 <> all (array_remove(array[col1, col2, col4, col5, col6], null))) and
(col4 is null or col4 <> all (array_remove(array[col1, col2, col3, col5, col6], null))) and
(col5 is null or col5 <> all (array_remove(array[col1, col2, col3, col4, col6], null))) and
(col6 is null or col6 <> all (array_remove(array[col1, col2, col3, col4, col5], null)))
)
我确实认为有更好的方法来组织数据,每个 col
和 id
一行。那将是一个新的 table。也就是说,很难对强制执行的三个值施加约束。
注意:您现有的约束也可以在 Postgres 中进行简化:
ALTER TABLE my_table
ADD CONSTRAINT my_constraint CHECK (
cardinality(array_remove(array[col2, col3, col4, col5, col6], null)) >= 3
)
据我所知,这些实际上是三个不同的约束。必须存在至少三个非空值的方法很简单:
ALTER TABLE my_table
ADD CONSTRAINT my_constraint CHECK ( num_nonnulls(c1, c2, c3, c4, c5, c6) >= 3);
单行的 6 列中不应存在重复值的要求可以通过创建一个函数来检查:
create or replace function all_unique(p_row my_table)
returns boolean
as
$$
select not exists (
select v
from (
values (p_row.c1), (p_row.c2), (p_row.c3), (p_row.c4), (p_row.c5), (p_row.c6)
) t(v)
where v is not null
group by v
having count(*) > 1);
$$
language sql
immutable
strict;
函数 returns 如果所有非空值在一行中都是唯一的,则为真。我将其定义为接收完整的行,以便在添加或删除新列时更容易适应。我不确定这是否是最有效或最简单的方法,但我现在想不出别的方法。
这可用于检查约束:
ALTER TABLE my_table
ADD CONSTRAINT all_unique CHECK ( all_unique(my_table) );
最后一个要求,非空值在多行中应该是唯一的,可以用唯一索引来实现。但是为此,我们需要一个函数来以排序的方式创建非空值数组:
create or replace function non_null_values(p_row my_table)
returns text[]
as
$$
select array(
select distinct v
from (
values (p_row.c1), (p_row.c2), (p_row.c3), (p_row.c4), (p_row.c5), (p_row.c6)
) t(v)
where v is not null
order by v);
$$
language sql
immutable
strict;
为什么要排序?因为 Postgres 认为数组 [1,2,3]
不等于 (=
) 数组 [3,1,2]
,这是唯一索引使用的。
此函数可用于唯一索引:
create unique index on my_table (non_null_values(my_table));