PostgreSQL:是否可以将枚举转换为整数?

Alb*_*tti 19 postgresql

是否可以将枚举转换为整数?从第一个元素开始

Cra*_*ger 27

虽然您不能像Catcall解释的那样将枚举转换为整数,但您可以使用PostgreSQL特定的,可能不是版本与版本兼容的pg_enum系统目录表来获取序数表示.

regress=# CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');

regress=# select enumsortorder, enumlabel from pg_catalog.pg_enum 
regress-# WHERE enumtypid = 'happiness'::regtype ORDER BY enumsortorder;
 enumsortorder | enumlabel  
---------------+------------
             1 | happy
             2 | very happy
             3 | ecstatic
(3 rows)
Run Code Online (Sandbox Code Playgroud)

这看起来很简单,但事实并非如此.注意:

regress=# ALTER TYPE happiness ADD VALUE 'sad' BEFORE 'happy';
regress=# ALTER TYPE happiness ADD VALUE 'miserable' BEFORE 'very happy';
regress=# SELECT * FROM pg_enum ;
 enumtypid | enumsortorder | enumlabel  
-----------+---------------+------------
    185300 |             1 | happy
    185300 |             2 | very happy
    185300 |             3 | ecstatic
    185300 |             0 | sad
    185300 |           1.5 | miserable
(5 rows)
Run Code Online (Sandbox Code Playgroud)

从这里您可以看到enumsortorder提供订购,但没有固定的"距离".如果添加了从枚举中删除值的支持,它也可能会在序列中创建"漏洞".

要获得枚举位置,您需要使用row_number()窗口函数来获取排序,并pg_typeof获取枚举类型的oid(regtype).当有多个具有相同标签的枚举时,您需要这样做以确保返回正确的序号.

这个功能完成了这项工作:

CREATE OR REPLACE FUNCTION enum_to_position(anyenum) RETURNS integer AS $$
SELECT enumpos::integer FROM (
        SELECT row_number() OVER (order by enumsortorder) AS enumpos,
               enumsortorder,
               enumlabel
        FROM pg_catalog.pg_enum
        WHERE enumtypid = pg_typeof($1)
    ) enum_ordering
    WHERE enumlabel = ($1::text);
$$ LANGUAGE 'SQL' STABLE STRICT;
Run Code Online (Sandbox Code Playgroud)

注意:

  • STABLE不是IMMUTABLE,因为从枚举中添加(或者如果稍后添加Pg中的支持,删除)值会改变依赖于排序的排序和中断索引; 所以
  • 你不能在索引表达式中使用它; 和
  • 这是STRICT因为它应该为null输入返回null

您现在可以将此功能CREATE CAST用于特定的枚举integer.您无法为所有枚举创建通用强制转换integer,因为anyenum伪类型不能用于强制转换.例如,如果我想允许将演示happiness转换为整数,我会写:

CREATE CAST (happiness AS integer) WITH FUNCTION enum_to_position(anyenum);
Run Code Online (Sandbox Code Playgroud)

之后我可以成功执行:

regress=# SELECT ('happy'::happiness)::integer;
 int4 
------
    2
(1 row)
Run Code Online (Sandbox Code Playgroud)

请注意,这可能是一件疯狂的事情,不受支持,很可能是一个糟糕的主意.您的代码必须注意,当您添加或(如果以后支持)从枚举中删除值时,序数值将会更改.

基于此强制转换创建的索引(仅当函数定义为不可变时才可能)如果更改枚举的定义(除了通过在其末尾添加新值),将开始产生疯狂和错误的结果,因为PostgreSQL在您说时会认为您函数是不可变的.不要那样做.

  • @Skartt 嗯,9.1 很古老且不受支持,如果您使用任何旧版本,那么升级已经晚了好几年...... (2认同)

mpo*_*tes 18

你可以通过巧妙滥用函数enum_range()来做到这一点.

如果将枚举值作为enum_range()函数的第二个参数传递,并且NULL是第一个,那么您将得到一个数组,其中包含枚举可以占用的所有值.然后你只需要用array_length来计算它们,你就得到一个表示枚举的整数.

这是一个例子.这是我的枚举:

content=# select enum_range(null::content_state);
                        enum_range                          
--------------------------------------------------------------
 {created,deleted,preview,draft,submitted,approved,published}
Run Code Online (Sandbox Code Playgroud)

这就是我为"草案"值找出int:

content=# select array_length(enum_range(NULL, 'draft'::content_state), 1);
 array_length 
--------------
            4
Run Code Online (Sandbox Code Playgroud)

警告:从枚举中删除值将使您的int指向其他值,因此不要在您可能希望在某些时候更改的枚举上使用它.


Mik*_*ll' 5

您不能将枚举转换为整数。

也许可以编写一个自定义运算符来提取与某个值关联的数字,但我发现很难相信这是值得的。

如果我需要此类信息,我会构建一个表并为其设置外键引用,而不是使用枚举。