PostgreSQL 查询约束中允许的值列表?

问题描述 投票:0回答:4

给定一个名为

requests
的 PostgreSQL 表,其中包含名为
status
的列和如下约束:

ALTER TABLE requests ADD CONSTRAINT allowed_status_types
  CHECK (status IN (
    'pending', -- request has not been attempted
    'success', -- request succeeded
    'failure'  -- request failed
  ));

psql
中,我可以像这样提取有关此约束的信息:

example-database=# \d requests
                                          Table "public.example-database"
        Column        |            Type             |                             Modifiers
----------------------+-----------------------------+-------------------------------------------------------------------
 id                   | integer                     | not null default nextval('requests_id_seq'::regclass)
 status               | character varying           | not null default 'pending'::character varying
 created_at           | timestamp without time zone | not null
 updated_at           | timestamp without time zone | not null

Indexes:
    "requests_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "allowed_status_types" CHECK (status::text = ANY (ARRAY['pending'::character varying, 'success'::character varying, 'failure'::character varying]::text[]))

但是是否可以编写一个专门返回待处理、成功、失败的

allowed_status_types
的查询?

如果能够在我的应用程序中记住此查询的结果,而不是必须维护重复的副本,那就太好了。

sql database postgresql database-design check-constraints
4个回答
6
投票

以您的设计为基础

为了简化事情,我将提供允许的值(100%等效)数组文字而不是

IN
表达式(转换为笨拙的ARRAY构造函数):

ALTER TABLE requests ADD CONSTRAINT allowed_status_types
  CHECK (status = ANY ('{pending, success, failure}'::text[]));

系统栏中的结果文本

pg_constraint.consrc
:

((status)::text = ANY ('{pending,success,failure}'::text[]))

现在可以很简单地使用

substring()
提取大括号之间的列表:

SELECT substring(consrc from '{(.*)}') AS allowed_status_types
FROM   pg_catalog.pg_constraint
WHERE  conrelid = 'public.requests'::regclass  -- schema qualify table name!
AND    conname = 'allowed_status_types';  -- we *know* the constraint name

结果:

  allowed_status_types
-------------------------
 pending,success,failure

另类设计

我真正想做的就是再规范化一步:

CREATE TABLE request_status (
  status_id "char" PRIMARY KEY
, status text UNIQUE NOT NULL
, note text
);

INSERT INTO request_status(status_id, status, note) VALUES
  ('p', 'pending', 'request has not been attempted')
, ('s', 'success', 'request succeeded')
, ('f', 'failure', 'req');

CREATE TABLE requests (
  id         serial PRIMARY KEY
, status_id "char" NOT NULL DEFAULT 'p' REFERENCES request_status
, created_at timestamp NOT NULL
, updated_at timestamp NOT NULL
);

数据类型

"char"
是单个单字节 ASCII 字符,非常适合廉价枚举少数可能值。

行的大小现在是 48 个字节,而不是 56 个字节。 详情请看这里。
检查允许的状态很简单:

SELECT status FROM request_status

5
投票

您可以查询系统目录

pg_constraint
,例如:

select consrc
from pg_constraint
where conrelid = 'requests'::regclass
and consrc like '(status%';

                                  consrc                                   
---------------------------------------------------------------------------
 (status = ANY (ARRAY['pending'::text, 'success'::text, 'failure'::text]))
(1 row) 

使用以下函数解压字符串:

create or replace function get_check_values(str text)
returns setof text language plpgsql as $$
begin
    return query
        execute format (
            'select * from unnest(%s)',
            regexp_replace(str, '.*(ARRAY\[.*\]).*', '\1'));
end $$;

select get_check_values(consrc)
from pg_constraint
where conrelid = 'requests'::regclass
and consrc like '(status%';

 get_check_values 
------------------
 pending
 success
 failure
(3 rows)    

0
投票

您可以创建一个新表

status_types
并将表
status
的列
request
迁移为表
status_types
的外键。

通过这种方式,您可以进行通常的完整性检查 - 这是一个可移植的解决方案,适用于其他关系数据库。

这样就可以轻松编写专门返回允许值的查询。

并且扩展允许的状态类型列表非常容易。


0
投票

consrc
在 Postgres 12+ 中被删除。如果有人在这些版本中遇到此问题,那么以下是解决方案:

可以使用以下查询列出某个表的约束及其详细信息。

SELECT
    conname AS constraint_name,
    contype AS constraint_type,
    conkey AS constrained_columns,
    confkey AS referenced_columns,
    confrelid::regclass AS referenced_table,
    conrelid::regclass AS constrained_table,
    CASE
        WHEN contype = 'f' THEN 'FOREIGN KEY'
        WHEN contype = 'u' THEN 'UNIQUE'
        WHEN contype = 'p' THEN 'PRIMARY KEY'
        WHEN contype = 'c' THEN 'CHECK'
    END AS constraint_category,
    CASE
        WHEN contype = 'c' THEN
            (SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE oid = c.oid)
    END AS check_expression
FROM
    pg_constraint c
WHERE
    conrelid = 'table_name'::regclass;
© www.soinside.com 2019 - 2024. All rights reserved.