PostgreSQL: возможно ли, чтобы enum перечислил целое число?

Возможно ли, что enum перечисляет целое число? Начиная с 1 первого элемента

Ответ 1

Пока вы не можете использовать enum для integer в качестве объяснения 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)

Это выглядит легко, но это не так. Обратите внимание:

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)

Из этого вы можете видеть, что 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;

Примечание:

  • STABLE not IMMUTABLE, потому что добавление (или добавление поддержки в Pg позже добавляется, удаление) значений из перечислений изменяет индексы упорядочения и разрыва, основанные на упорядочении; поэтому
  • Вы не можете использовать это в выражении индекса; и
  • It STRICT, потому что он должен возвращать значение null для ввода с нулевым значением

Теперь вы можете использовать эту функцию для CREATE CAST для определенных перечислений на integer. Вы не можете создать общий набор для всех перечислений на integer, потому что псевдо-тип anyenum не может использоваться для трансляций. Например, если я хочу, чтобы демонстрационная версия happiness была добавлена ​​в целое число, я бы написал:

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

после которого я мог бы успешно выполнить:

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

Обратите внимание, что это, наверное, безумная вещь, не поддерживается, и, вполне вероятно, это ужасная идея. Ваш код должен знать, что порядковые значения будут меняться при добавлении или (если позже будут поддерживаться) удалите значение из перечисления.

Индексы, созданные на основе этого приведения (возможно только в том случае, если функция определена неизменяемой) начнут производить безумные и неправильные результаты, если вы измените определение перечисления (за исключением добавления новых значений до конца), поскольку PostgreSQL полагает, что вы когда вы говорите, что функция неизменна. Не делайте этого.

Ответ 2

Вы можете сделать это с помощью умного злоупотребления функцией enum_range().

Если вы передадите значение enum в качестве второго аргумента функции enum_range(), при этом NULL будет первым, вы получите массив со всеми значениями, которые перечисление может занимать до этой точки. Затем вам просто нужно подсчитать их с помощью array_length, и вы получите целое число, представляющее перечисление.

Вот пример. Это мое перечисление:

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

И это я выясняю int для значения "draft":

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

Caveat: удаление значений из enum приведет к тому, что ваши int указывают на другие значения, поэтому не используйте это в перечислениях, которые вы, возможно, захотите изменить в какой-то момент.

Ответ 3

Вы не можете преобразовать перечисление в целое число.

Возможно, вы сможете написать пользовательский оператор, чтобы извлечь номер, связанный со значением, но мне трудно поверить, что стоит беда.

Если бы мне нужна такая информация, я бы построил таблицу и установил для нее ссылку на внешний ключ вместо использования перечисления.

Ответ 4

Да, вы можете.

Если ваш тип перечисления содержит только целочисленное значение, то листинг становится очень простым. Предположим, что у меня есть тип перечисления как: - Create type monthenum as enum ('7', '8', '9', '10', '11', '12', '1', '2', '3', '4', '5', '6');

Теперь вы можете использовать значения перечисления для целого числа и отображать их как отдельную запись следующим образом: -

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

Надеюсь, это вам поможет. Идея состоит в том, что вы можете преобразовать перечисление в текст, а затем снова в любые другие совместимые типы вашего значения перечисления. Вы также можете использовать для цикла для преобразования значений один за другим.