web-dev-qa-db-ja.com

同じクエリで日付とタイムスタンプをタイムゾーンとnow()で比較しますか?

有効期限列をnow()と比較するクエリを使用してクエリを実行している複数のデータベースサーバーがあります。問題は、サーバーの有効期限列の1つがtimestamp with time zoneであり、残りはすべてdateであるということです。管理アクセス権がないため、これを変更することはできません。実際、ビューをクエリするだけです。 Postgresは私にとってかなり新しいものなので、日付と時刻が互いにどのように連携するのか私にはよくわかりません。

timestampdateとしてキャストすることにより、timestamp with time zoneを使用してサーバーにクエリを実行すると、次のようになります。

...
WHERE
    (
        status_code = '30000'
        OR status_code = '30005'
    )
AND CAST(expiration AS DATE) > now()

機能しますが、expirationがすでにdateであるサーバーで同じクエリを使用すると失敗します。

[エラー]エラー:タイプ日付の無効な入力構文:「終了日なし」

手助けをいただければ幸いですが、実際には、この1つのDBサーバーの例外をハードコードしないでください。

6
Chris

これは決して失敗しないはずです(私は少し簡略化しました):

_WHERE status_code IN ('30000','30005')
AND   expiration > now() 
_

PostgreSQLは datetimestamp(タイムゾーンの有無にかかわらず) を自動的に比較できます。 dateの場合は、自動的にtimestampにキャストされます(0:0時間)。

エラーメッセージは別の話を伝えます。無効な構文でdateを入力しようとしています。

最近、PostgreSQLのタイムゾーンの有無に関係なくタイムスタンプの処理について詳細な回答を書きました。


結局のところ、expirationtext列です。 dateまたはtimestamp(必要に応じて)にキャストする必要があります。有効な形式の場合:

_...
AND   expiration::timestamptz > now() 
_

そのテキスト列に「終了日なし」などの無効な文字列がある場合、ソースをクリーンアップするか、CASE構成で特別に処理する必要があります。

_...
AND   CASE WHEN expiration::text = 'No End Date' THEN 'infinity'::timestamp
           WHEN expiration::text = 'foo' THEN '-infinity'::timestamp
           ELSE expiration::timestamp
      END > now()
_

特別な値に関するマニュアルinfinity

テキストへのキャスト(_::text_)はtextと冗長ですが、式はdate/timestamp値でも機能します。

タイムスタンプリテラルの形式があいまいな場合は、to_date()またはto_timestamp()を使用して、形式を明示的に定義します。

_to_date('07/08/2013', 'DD/MM/YYYY')
_

expirationanotherタイムゾーンのローカルタイムスタンプであると想定されている場合は、 _AT TIME ZONE_構文 を使用します。

_expiration::timestamp AT TIME ZONE 'UTC' -- desired time zone here
_

expirationがあいまいな/非標準の形式の場合、 to_timestamp() を使用します。

_to_timestamp(expiration::text, 'yyyy-mm-dd')
_
8