PostgreSQL - 对数组中每个自定义类型元素的约束

时间:2017-06-11 10:16:37

标签: postgresql

假设我有这种自定义类型:

CREATE TYPE post AS (
  title varchar(100),
  content varchar(10000)
);

这张表:

CREATE TABLE blogs (
  -- Constraint on one custom type element:
  one_post post NOT NULL
    CHECK ((one_post).title IS NOT NULL AND (one_post).content IS NOT NULL),

  -- Constraint on array elements:
  posts post[] NOT NULL CHECK (???)
);

如代码中所述,我想检查posts的每个元素的标题和内容是否为NOT NULL,但我找不到表达方式。这样做的正确方法是什么?

(P.S。:我知道正确的方法是规范化我的架构,我只是好奇我能提出的问题。)

1 个答案:

答案 0 :(得分:3)

对于复杂的检查约束,通常使用存储的函数。

我尝试以方便的方式制作它:

首先:

create type post as (
  title varchar(100),
  content varchar(10000)
);

create function is_filled(post) returns bool immutable language sql as $$
  select $1.title is not null and $1.content is not null
$$;

with t(x) as (values(('a','b')::post), (('a',null)::post), ((null,null)::post))
select *, is_filled(x), (x).is_filled from t;
╔═══════╤═══════════╤═══════════╗
║   x   │ is_filled │ is_filled ║
╠═══════╪═══════════╪═══════════╣
║ (a,b) │ t         │ t         ║
║ (a,)  │ f         │ f         ║
║ (,)   │ f         │ f         ║
╚═══════╧═══════════╧═══════════╝

在这里你可以看到函数调用的两种语法(我个人喜欢第二种 - 它更像OOP)

下一步:

create function is_filled(post[]) returns bool immutable language sql as $$
  select bool_and((x).is_filled) and $1 is not null from unnest($1) as x
$$;

with t(x) as (values(('a','b')::post), (('a',null)::post), ((null,null)::post))
select (array_agg(x)).is_filled from t;
╔═══════════╗
║ is_filled ║
╠═══════════╣
║ f         ║
╚═══════════╝

请注意,我们可以使用具有相同名称但具有不同参数的函数 - 在PostgreSQL中可以使用。

最后:

create table blogs (
  one_post post check ((one_post).is_filled),
  posts post[] check ((posts).is_filled)
);

insert into blogs values(('a','b'), array[('a','b'),('c','d')]::post[]); -- Works
insert into blogs values(('a','b'), array[('a','b'),(null,'d')]::post[]); -- Fail
insert into blogs values(('a',null), array[('a','b')]::post[]); -- Fail

PS:清除我们实验的脚本:

/*
drop table if exists blogs;
drop function if exists is_filled(post);
drop function if exists is_filled(post[]);
drop type if exists post;
*/