【问题标题】:Algebraic Data Types in PostgresPostgres 中的代数数据类型
【发布时间】:2017-11-09 22:24:42
【问题描述】:

是否可以在 Postgres 中创建代数数据类型,然后将其用作列类型?

例如:

CREATE TYPE hoofed AS ENUM('horse', 'goat');

CREATE TYPE monkey AS ENUM('chimp','macaque');

CREATE TYPE ANIMAL AS ENUM(hoofed, monkey);

这失败了:

syntax error at or near "hoofed"
LINE 1: CREATE TYPE ANIMAL AS ENUM(hoofed, monkey);

有可能做这样的事情吗?

最终我希望能够做到的事情是这样的:

CREATE TABLE zoo (
    a ANIMAL,
    name text
);

INSERT INTO zoo(a, name) VALUES('horse', 'bob');
INSERT INTO zoo(a, name) VALUES('macaque', 'jimmy');

并且两个记录都是独立有效的。

编辑:@Abihabi87 下面的回复确实允许我创建一个产品类型,但它仍然不允许我根据需要创建一个联合类型。

【问题讨论】:

    标签: sql postgresql types enums union-types


    【解决方案1】:

    你不能从其他枚举类型创建类型枚举:

    你可以创建这样的动物:

    CREATE TYPE ANIMAL AS (h hoofed,m monkey);
    

    使用示例:

    CREATE TABLE your_table
    (
        a ANIMAL
    );
    
    INSERT INTO your_table(a) select (select ('horse','macaque')::ANIMAL);
    

    【讨论】:

    • 我将如何使用它作为列类型?创建表示例( col1 ANIMAL );插入示例(col1)值(“马”);抛出异常:[ERROR] 错误:格式错误的记录文字:“马”
    • 所以这实际上会创建如下记录:SELECT * FROM your_table; a ----------------- (horse,macaque) (1 row) 我真正追求的是以下两个都独立有效.. a ---- ------------- 'horse' a ----------------- 'macaque' 为了清楚起见,我已经更新了问题
    • 我认为这是赏金的正确答案。这是他应得的。
    • @AsifAli 它没有回答原始问题。建议的答案不能正确验证数据。我们需要 postgres 来确保对于任何给定的 x::ANIMAL,只有一个字段不为空。如果在 Postgres 中无法确保这一点,那么答案必须这样说才能解决原始问题。
    【解决方案2】:

    使用函数:

    create or replace function create_enum(name, variadic regtype[])
    returns void language plpgsql as $$
    begin
        execute format(
            'create type %I as enum(%s)', 
            $1, 
            string_agg(quote_literal(enumlabel), ',' order by enumtypid, enumsortorder))
        from pg_enum
        where enumtypid = any($2);
    end $$;
    

    将新类型的名称和枚举类型列表作为参数传递:

    select create_enum('animal', 'hoofed', 'monkey');
    
    select enum_range(null::animal) as animal;
    
               animal           
    ----------------------------
     {horse,goat,chimp,macaque}
    (1 row)
    

    【讨论】:

      【解决方案3】:

      实际上,您正在尝试合并两个 enum 类型。
      有一些悬而未决的问题:

      • 可以有重复的字符串吗?
      • 设计应该是静态(更改为enum 类型hoofed 以后不会更改类型animal)还是动态(相反)。
      • 恰好合并两个或更多 enum 类型?
      • 既然元素的顺序很重要,那么animal 中的元素顺序应该是什么?
      • 这是一次性操作还是打算重复使用?

      假设没有重复,静态设计,两个enum类型,元素的现有顺序作为附加和一次性操作。

      您可以使用内置的enum support function enum_range(anyenum) 来获取给定enum 类型的所有元素的数组。

      DO
      $$
      BEGIN
      EXECUTE (
         SELECT 'CREATE TYPE animal AS ENUM (' 
              || array_to_string(enum_range(null::hoofed)::text[]
                              || enum_range(null::monkey)::text[], ''',''')
              || ''')'
         );
      END
      $$;
      

      【讨论】:

      • 这真的很酷!未来对底层 ENUM 的更新是否可以动态更新新的 ENUM?
      • @AbrahamP:不,这是不可能的。您可以删除并重新创建 animal 类型,也可以编写服务器端代码来动态添加/删除元素,但您需要首先考虑对现有数据的依赖关系和影响。
      • @AbrahamP A DDL event trigger 应该能够做到这一点。
      【解决方案4】:

      使用ENUM 类型,您无法实现动态类型组合/联合。但是,使用DOMAIN types,您可以实现类似的效果:

      create function valid_any_domain(anyelement, variadic regtype[])
        returns boolean
        language plpgsql
        immutable
      as $func$
      declare
        t regtype;
      begin
        foreach t in array $2 loop
          begin
            execute format('select $1::%s', t) using $1;
          exception
            when not_null_violation or check_violation then
              continue;
          end;
          return true;
        end loop;
      
        return false;
      end;
      $func$;
      
      create domain hoofed as text
        check (value in ('horse', 'goat'));
      
      create domain monkey as text
        check (value in ('chimp','macaque'));
      
      create domain animal as text
        check (valid_any_domain(value, 'hoofed', 'monkey'));
      

      更改基本类型也会动态更改复合/联合类型,但仍需要手动约束验证(尤其是当某些值从有效频谱中删除时):

      alter domain hoofed drop constraint hoofed_check;
      alter domain hoofed add check (value in ('horse', 'goat', 'zebra'));
      alter domain animal validate constraint animal_check;
      

      http://rextester.com/MBVC62095

      注意:但是,对于 DOMAIN 类型,您将失去 ENUM 属性:自定义排序。 DOMAINs 将始终使用底层类型的排序。

      【讨论】:

        猜你喜欢
        • 2015-07-17
        • 1970-01-01
        • 2017-09-27
        • 1970-01-01
        • 2012-11-20
        • 2016-07-19
        • 2018-04-25
        • 2020-07-01
        • 1970-01-01
        相关资源
        最近更新 更多