21

列挙型を整数にキャストすることは可能ですか?1から始まる最初の要素

4

3 に答える 3

34

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;

ノート:

  • 列挙型から値を追加する(またはPgのサポートが後で追加される場合は削除する)と、順序が変更され、順序に依存してインデックスが破損するため、そうではありませSTABLEん。IMMUTABLEそれで
  • これをインデックス式で使用することはできません。と
  • これSTRICTは、null入力に対して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があなたを信じているため、クレイジーで間違った結果を生成し始めます。関数は不変です。そうしないでください。

于 2012-09-10T08:17:13.857 に答える
22

関数enum_range()を使用して、その結果をカウントすることに満足している場合は、これを行うことができます。

enum_range()関数の2番目の引数としてenum値を渡し、NULLを最初に指定すると、enumがその時点までに取ることができるすべての値を含む配列が取得されます。次に、array_lengthでそれらをカウントする必要があり、列挙型を表す整数を取得します。

これが例です。これは私の列挙型です:

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

そして、これは私が「ドラフト」値の整数を理解していることです。

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

警告:列挙型から値を削除すると、intが他の値を指すようになるため、ある時点で変更する可能性のある列挙型ではこれを使用しないでください。

于 2013-04-16T18:53:16.777 に答える
4

列挙型を整数にキャストすることはできません。

値に関連付けられた数値を抽出するカスタム演算子を作成できる場合もありますが、それが問題の価値があるとは信じがたいです。

そのような情報が必要な場合は、列挙型を使用する代わりに、テーブルを作成して外部キー参照を設定します。

于 2012-09-10T03:36:56.517 に答える