【问题标题】:PostgreSQL query for a list of allowed values in a constraint?PostgreSQL查询约束中的允许值列表?
【发布时间】:2016-05-03 18:56:37
【问题描述】:

给定一个名为 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


    【解决方案1】:

    您可以查询系统目录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)    
    

    【讨论】:

      【解决方案2】:

      以您的设计为基础

      为了简单起见,我将提供允许的值作为(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
      );
      

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

      您的行大小现在是 48 而不是 56 字节。 Details here.
      检查允许的状态很简单:

      SELECT status FROM request_status
      

      【讨论】:

        【解决方案3】:

        您可以创建一个新表 status_types 并将表 request 的列 status 迁移为表 status_types 的 ForeignKey。

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

        这样很容易编写专门返回允许值的查询。

        而且很容易扩展允许的状态类型列表。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-23
          • 1970-01-01
          相关资源
          最近更新 更多