如何确保postgresql中只有一列不为空table

How to make sure only one column is not null in postgresql table

我正在尝试设置 table 并向其添加一些约束。我打算使用部分索引来添加约束来创建一些复合键,但是 运行 进入了处理 NULL 值的问题。我们有这样一种情况,我们想要确保在 table 中,只有两列中的一列被填充给定行,并且填充的值是唯一的。我想弄清楚如何做到这一点,但我很难过。也许是这样的:

CREATE INDEX foo_idx_a ON foo (colA) WHERE colB is NULL
CREATE INDEX foo_idx_b ON foo (colB) WHERE colA is NULL

这行得通吗?另外,有没有好的方法可以将其扩展到更多的列?

您可以使用以下检查:

create table table_name 
(
  a integer, 
  b integer, 
  check ((a is null) != (b is null))
);

如果有更多列,您可以使用将 boolean 转换为 integer 的技巧:

create table table_name 
(
  a integer, 
  b integer,
  ...
  n integer,
  check ((a is not null)::integer + (b is not null)::integer + ... + (n is not null)::integer = 1)
);

在此示例中,只有一列可以不为空(它只是计算非空列),但您可以将其设为任意数字。

可以使用 insert/update 触发器或 来做到这一点,但必须这样做表明它可以做得更好。约束的存在是为了让您确定自己的数据,因此您不必经常检查数据是否有效。如果一个或另一个不为空,则必须在查询中进行检查。

使用 table inheritance 和浏览量可以更好地解决这个问题。

假设您有(美国)客户。有些是企业,有些是个人。每个人都需要一个 Taxpayer Identification Number,它可以是社会安全号码或雇主识别号码等多种内容之一。

create table generic_clients (
  id bigserial primary key,
  name text not null
);

create table individual_clients (
  ssn numeric(9) not null
) inherits(generic_clients);

create table business_clients (
  ein numeric(9) not null
) inherits(generic_clients);

SSN 和 EIN 都是纳税人识别号,您可以创建一个将两者一视同仁的视图。

create view clients as
  select id, name, ssn as tin from individual_clients
  union
  select id, name, ein as tin from business_clients;

现在您可以查询 clients.tin,或者如果您特别想要查询 business_clients.ein 的企业和查询 individual_clients.ssn 的个人。您还可以看到如何扩展继承的表以容纳不同类型的客户端之间更多不同的信息。

编写此约束的另一种方法是使用 num_nonulls() 函数:

create table table_name 
(
  a integer, 
  b integer, 
  check ( num_nonnulls(a,b) = 1)
);

如果您有更多列,这将特别有用:

create table table_name 
(
  a integer, 
  b integer, 
  c integer,
  d integer,
  check ( num_nonnulls(a,b,c,d) = 1)
);