Sql 使行中的所有非空值唯一
我有一个包含以下列的表:id、col1、col2、col3、col4、col5、col6 约束表示至少有3列被填充(因此最多有3个空值)。(列不是按顺序填充的,因此可以有col1、col2、col5填充,col3、col4、col6为空) 如何确保该列不为NULL时,它在此行的其他列中是唯一的? 如何确保非空值的组合在所有行中都是唯一的 我当前添加了以下约束(以确保至少3个非空):Sql 使行中的所有非空值唯一,sql,postgresql,check-constraints,Sql,Postgresql,Check Constraints,我有一个包含以下列的表:id、col1、col2、col3、col4、col5、col6 约束表示至少有3列被填充(因此最多有3个空值)。(列不是按顺序填充的,因此可以有col1、col2、col5填充,col3、col4、col6为空) 如何确保该列不为NULL时,它在此行的其他列中是唯一的? 如何确保非空值的组合在所有行中都是唯一的 我当前添加了以下约束(以确保至少3个非空): 这不是一个有趣的约束,因为所有的NULL值都是空的,但我认为这样做了 alter table my_table a
这不是一个有趣的约束,因为所有的
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)))
)
这不是一个有趣的约束,因为所有的
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)))
)
据我所知,这实际上是三个不同的约束条件。至少必须存在三个非空值的方法很简单:
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;
如果一行中的所有非空值都是唯一的,则函数返回true。我将其定义为接收一个完整的行,以便在添加或删除新列时更容易适应。我不确定这是否是最有效或最简单的方法,但我现在想不出其他方法
这可用于检查约束:
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));
据我所知,这实际上是三个不同的约束条件。至少必须存在三个非空值的方法很简单:
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;
如果一行中的所有非空值都是唯一的,则函数返回true。我将其定义为接收一个完整的行,以便在添加或删除新列时更容易适应。我不确定这是否是最有效或最简单的方法,但我现在想不出其他方法
这可用于检查约束:
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));
我的第一反应是,您可能想更改您的桌子设计。与其有六列,不如有一列,再加上第二列以跟踪原始“列”。@TimBiegeleisen恐怕我不明白您的点有一个检查约束,每个非空列加1,并验证总和至少为3。@jarlh但唯一的要求是什么?这是一种跨行而不是跨列聚合的方法。@TimBiegeleisen,哦,错过了这一部分。我的第一反应是您可能想更改表设计。与其有六列,不如有一列,再加上第二列以跟踪原始“列”。@TimBiegeleisen恐怕我不明白您的点有一个检查约束,每个非空列加1,并验证总和至少为3。@jarlh但唯一的要求是什么?这是跨行而不是跨列聚合的问题。@TimBiegeleisen,oops,遗漏了这一部分。可以简化现有约束,以注意当前约束可以简化为
检查((num_nonnulls(col1,col2,col3,col4,col5,col6)>=3))
谢谢您的回答。实际上,我目前的结构是每列和每id一行。这种方法的问题是:多个其他表与此表相关(other\u table有许多My\u table
),我不想在每次插入/删除时使用触发器约束锁定表来检查最小数量的相关记录。所以我确信这种结构会缓解这个问题在ORM级别验证要容易得多(因为我验证单个记录的字段,而不是3到6个记录的字段)。但我也希望确保数据库级的完整性,即cselect'foo'any(array['foo','bar')代码>产生t
,而它应该是f
我相信现有约束可以简化为注意当前约束可以简化为检查((num_nonnulls(col1,col2,col3,col4,col5,col6)>=3))
谢谢你的回答。实际上,我目前的结构是每列和每id一行。这种方法的问题是:多个其他表与此表相关(other\u table有许多My\u table
),我不想在每次插入/删除时使用触发器约束锁定表来检查最小数量的相关记录。所以我确信这种结构会缓解这个问题在ORM级别验证要容易得多(因为我验证单个记录的字段,而不是3到6个记录的字段)。但我也希望确保数据库级的完整性,即cselect'foo'any(array['foo','bar')代码>产生t
,而它应该是f
我相信谢谢你的回答!当我试着运行它时