向PostgreSQL添加约束以检查一列(来自一组列)是否包含非空值的好方法是什么?
更新:我可能希望使用Create Table和Alter Table中详述的check
表达式。
更新:我正在查看可用的functions。
更新:仅供后台使用,以下是我目前正在使用的Rails验证逻辑:
validate :multi_column_validation
def multi_column_validation
n = 0
n += 1 if column_1
n += 1 if column_2
n += 1 if column_3
unless 1 == n
errors.add(:base, "Exactly one column from " +
"column_1, column_2, column_3 must be present")
end
end
要明确的是,我在寻找PSQL,而不是Ruby。我只想展示我正在使用的逻辑,因为它比枚举所有“真值表”的可能性更紧凑。
答案 0 :(得分:34)
根据"constraint -- one or the other column not null" PostgreSQL message board:
,这是一个优雅的双列解决方案ALTER TABLE my_table ADD CONSTRAINT my_constraint CHECK (
(column_1 IS NULL) != (column_2 IS NULL));
(但上述方法不能推广到三列或更多列。)
如果您有三列或更多列,则可以使用a_horse_with_no_name所示的真值表方法。但是,我认为以下内容更容易维护,因为您不必输入逻辑组合:
ALTER TABLE my_table
ADD CONSTRAINT my_constraint CHECK (
(CASE WHEN column_1 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN column_2 IS NULL THEN 0 ELSE 1 END) +
(CASE WHEN column_3 IS NULL THEN 0 ELSE 1 END) = 1;
为了压缩这个,创建一个自定义函数以便删除CASE WHEN column_k IS NULL THEN 0 ELSE 1 END
样板文件会很有用,留下类似的东西:
(non_null_count(column_1) +
non_null_count(column_2) +
non_null_count(column_3)) = 1
这可能与PSQL一样紧凑(?)。也就是说,如果可能的话,我更愿意使用这种语法:
non_null_count(column_1, column_2, column_3) = 1
答案 1 :(得分:17)
我认为最干净和通用的解决方案是创建一个函数来计算某些参数的空值。为此,您可以使用pseudo-type anyarray
和类似的SQL函数:
CREATE FUNCTION count_not_nulls(p_array anyarray)
RETURNS BIGINT AS
$$
SELECT count(x) FROM unnest($1) AS x
$$ LANGUAGE SQL IMMUTABLE;
使用该功能,您可以将CHECK CONSTRAINT
创建为:
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK(count_not_nulls(array[col1, col2, col3]) = 1);
仅当列具有相同的数据类型时才会起作用。如果不是这种情况,您可以将它们作为文本进行转换(因为您只关心空案例):
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK(count_not_nulls(array[col1::text, col2::text, col3::text]) = 1);
正如@muistooshort所记得的那样,你可以使用variadic arguments创建一个函数,这可以清楚地调用:
CREATE FUNCTION count_not_nulls(variadic p_array anyarray)
RETURNS BIGINT AS
$$
SELECT count(x) FROM unnest($1) AS x
$$ LANGUAGE SQL IMMUTABLE;
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK(count_not_nulls(col1, col2, col3) = 1);
答案 2 :(得分:16)
由mu is too short暗示:
alter table t
add constraint only_one_null check (
(col1 is not null)::integer + (col2 is not null)::integer = 1
)
答案 3 :(得分:3)
有点笨拙,但应该做的诀窍:
create table foo
(
col1 integer,
col2 integer,
col3 integer,
constraint one_is_not_null check
( (col1 is not null and col2 is null and col3 is null)
or (col1 is null and col2 is not null and col3 is null)
or (col1 is null and col2 is null and col3 is not null)
)
)
答案 4 :(得分:2)
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK (array_length(array_remove(ARRAY[col1, col2, col3], NULL), 1) = 1);
答案 5 :(得分:2)
自PostgreSQL 9.6起,您就有num_nonnulls
和num_nulls
comparison functions接受任意数量的VARIADIC参数。
例如,这将确保三列中只有一列为空。
ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK (num_nulls(col1, col2, col3) = 1);