web-dev-qa-db-ja.com

PostgreSQLのインデックスのタイムスタンプにキャストできないのはなぜですか?

外部ソースから受け取ったJSONドキュメントを使用しています。このドキュメントには、timestamp PostgreSQLデータ型のテキスト形式に対応するtimestampプロパティがあります。したがって、言い換えると、timestampタイプにキャストできます:(data->>'timestamp')::timestamp

そのtimestampプロパティをインデックスで使用したい。私は以下を試しました:

create table t
(
    data    jsonb
);

create index on t(((data->>'timestamp')::timestamp));

私は得ています:

ERROR:  functions in index expression must be marked IMMUTABLE

どうしてこれなの?ローカリゼーションの構成設定が静的ではないため、timestamptzへのキャストは不変ではないことを理解しましたが、そのロジックをtimestampへのキャストに適用できません。

ここでtimestampプロパティをインデックスにキャストする方法はまだありますか?

数日前にリリースされたPostgreSQL 12を使用しています。

1
basicsuperuser

textからtimestamp without time zoneへのキャストは、タイプ入力関数timestamp_in、つまりSTABLEを呼び出すことによって処理されます。

その理由は、timestamp_inが他のいくつかの形式もサポートしているためです。

SELECT 'now'::timestamp;

         timestamp          
----------------------------
 2019-10-09 09:53:32.026673
(1 row)

入力が常にISOタイムスタンプであり、これが発生しないことがわかっている場合は、独自の変換関数を定義します。

CREATE FUNCTION text2ts(text) RETURNS timestamp without time zone
   LANGUAGE sql IMMUTABLE AS
$$SELECT CASE WHEN $1 ~ '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$'
            THEN CAST($1 AS timestamp without time zone)
       END$$;

これをクエリとインデックスで使用できます。

パラノイアに欠けていて、データ品質に確信がある場合は、テストを省略できます。これによりパフォーマンスは向上しますが、誰かがnowのようなものをデータに忍び込むと、インデックスが破損する危険があります。

1
Laurenz Albe

試す

create table t
(
    data    jsonb,
    data_timestamp TIMESTAMP GENERATED ALWAYS AS ((data->>'timestamp')::timestamp) STORED
);

create index on t(data_timestamp);

コメントから:

私もそれを試しました(質問でそれを言及するのを忘れていました)、同じエラーが発生します:エラー:生成式は不変ではありません。

もしそうなら、私が見る唯一の方法は、その値がINSERTおよびUPDATEトリガーによって生成される静的列を使用することです。そしてそれによって索引を付けます。

0
Akina