2012-09-10 18 views

Respuesta

18

Aunque no se puede echar enumeración de número entero como se explica catcall, que puede utilizar el PostgreSQL-específica y posiblemente no-versión a versión compatible tabla catálogo pg_enum sistema para obtener una representación ordinal.

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) 

Esto parece fácil, pero no lo es. Observe:

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) 

De esto se puede ver que enumsortorder proporciona el pedido, pero no hay 'distancia' fija. Si alguna vez se agrega soporte para eliminar valores de las enumeraciones, es probable que también cree 'agujeros' en la secuencia.

para obtener la posición de enumeración que necesita para utilizar la función de row_number() ventana para conseguir el orden y la pg_typeof para obtener el oid (regtype) del tipo de enumeración. Necesita esto para asegurarse de devolver el ordinal correcto cuando hay varias enumeraciones con la misma etiqueta.

Esta función se encarga del trabajo:

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; 

Nota:

  • Es STABLE no IMMUTABLE, porque los valores de la adición (o si el apoyo en Pg después se añade, la eliminación) de las enumeraciones cambiarían el ordenar y romper índices confiando en el pedido; entonces
  • No puede usar esto en una expresión de índice; y
  • Es STRICT porque debe devolver null para una entrada nula

Ahora puede utilizar esta función para CREATE CAST para enumeraciones específicas para integer. No puede crear un molde genérico para todas las enumeraciones en integer, porque el pseudo-tipo anyenum no se puede usar para moldes. Por ejemplo, si quiero permitir que la demo happiness para ser echada a número entero, escribiría:

CREATE CAST (happiness AS integer) WITH FUNCTION enum_to_position(anyenum); 

después de lo cual pude ejecutar con éxito:

regress=# SELECT ('happy'::happiness)::integer; 
int4 
------ 
    2 
(1 row) 

Tenga en cuenta que esto es probablemente una loco cosa que hacer, no es compatible, y es muy probable que sea una idea terrible. Su código debe tener en cuenta que los valores ordinales cambiarán cuando agregue o (si lo admite posteriormente) eliminará un valor de la enumeración.

Los índices creados en base a este modelo (solo posible si la función se define como inmutable) comenzarán a producir resultados locos e incorrectos si cambia la definición de la enumeración (excepto agregando nuevos valores al final) porque PostgreSQL cree cuando dices que una función es inmutable. No hagas eso.

3

No se puede convertir una enumeración en número entero.

podría escribir un custom operator para extraer el número asociado a un valor, pero me parece difícil de creer que valga la pena.

Si necesitaba ese tipo de información, habría creado una tabla y establecido una referencia de clave externa en lugar de utilizar una enumeración.

11

Puede hacerlo mediante un inteligente abuso de la función enum_range().

Si pasa su valor enum como el segundo argumento de la función enum_range(), con NULL siendo el primero, obtendrá una matriz con todos los valores que enum puede llevar hasta ese punto. Entonces solo necesitas contarlos con array_length y obtienes un entero que representa la enumeración.

Aquí hay un ejemplo. Este es mi enumeración:

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

Y este soy yo averiguar el int para el "proyecto" valor:

content=# select array_length(enum_range(NULL, 'draft'::content_state), 1); 
array_length 
-------------- 
      4 

advertencia: la eliminación de los valores de la enumeración harán que su punto de enteros a otros valores, así que no uses esto en las enumeraciones que quieras cambiar en algún momento.

0

Sí, puedes.

Si su tipo de enumeración contiene solamente un valor entero, entonces la conversión se vuelve muy simple. Supongamos que tengo un tipo de enumeración como: - Create type monthenum as enum ('7', '8', '9', '10', '11', '12', '1', '2', '3', '4', '5', '6');

Ahora puede utilizar emitir los valores de enumeración a un entero y mostrarlos como registro separado de la siguiente manera: -

select (unnest(enum_range(null::monthenum))::text)::integer as monthvalue 

Espero que esto ayude, la idea es que puedes convertir la enumeración en texto y luego otra vez en cualquier otro tipo compatible de tu valor enum. También puede usar for loop para convertir los valores uno por uno.