web-dev-qa-db-ja.com

float-> numericキャストを代入から暗黙的、危険に変更しますか?

アプリケーションをPostgreSQL(9.1)に移植する際に私が発見した奇妙なSQLの非互換性の1つは、round()関数、特に丸めの精度を示す2番目の引数を取るバージョンに関係しています。

MySQLでは、round(some_float_column, 2)は期待どおりに機能し、some_float_columnの値を小数点以下2桁に丸めて返します。 PostgresではERROR: function round(double precision, integer) does not existでエラーが発生し、HINT: No function matches the given name and argument types. You might need to add explicit type casts.が提案されます。

ドキュメントで示されているように http://www.postgresql.org/docs/9.1/static/functions-math.html ... Postgresには2つのラウンド関数がありますround(value)は、倍精度または数値を受け取り、round(value, precision)は数値のみを取りますと整数。

だから、なぜ私は2引数形式のroundが最初にdoubleをとらないのか理解できませんが、何でも。検索で、この問題に対する2つの解決策を発見しました。 1つは、独自のバージョンのround(value, precision)を作成することです。これは、(double、int)を取り、既存の(numeric、int)バージョンを明示的なキャストでラップします。それは確かに機能しますが、私はそれが好きではありません(私のバックグラウンドは、真の浮動小数点型を持たないOracleです)。 float/doubleは暗黙的に数値にキャスト可能であるべきだと私には思われます。そして、これらのタイプのASSIGNMENTキャストが事前定義されていることがわかります。しかし、ここで見られるように、ASSIGNMENTは関数呼び出しでは機能しません。暗黙的にする必要があります。 thatの問題は、型のペアごとに1つのキャストしか定義できないことであり、float4-> numericおよびfloat8-> numeric代入キャストが必要ですシステムにより、ドロップできません。したがって、これらのキャストを暗黙的にする唯一の方法は、update pg_cast set castcontext = 'i' where castsource in (700,701) and casttarget = 1700を実行することです。

今、私たちはカタログをハッキングしています。これはこれがmightが悪い考えであることを示しています。しかし、私はそれが悪いという確固たる証拠はありません。 float/double値は不正確であり、数値は正確であるため、前者から後者へのキャストは完全にデータを保持するため、論理的に安全であるように思えます。私が知っている唯一の潜在的な問題は他の関数の引数パターンに曖昧さを導入することですが、この質問をする目的は主に潜在的な問題を見つけることですしないでください知っている。

それで、float-> numericキャスト動作をASSIGNMENTからIMPLICITに変更するのは危険ですか?

6
Noah Yetter

はい、カタログへのハッキングは悪いことです。理由#1は、新しいバージョンにアップグレードし、ハックを移動するのを忘れた場合、状況が悪化し始めることです。 pg_dumpを実行して、別のインスタンスで同じバージョンにロードするだけでも、ハックは失われます。また、Postgresの新しいバージョンが大幅に変更される可能性が常にあるため、ハックが不可能になり、戻って再設計する必要があります。

独自の関数でオーバーライドするのが正しい方法です。

3
Matthew Wood

異なるデータ型には異なる丸めアルゴリズムが使用されることに注意してください。

https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-TABLE

値を丸める場合、数値型はゼロから離れるように丸められますが、(ほとんどのマシンでは)実数および倍精度型は、最も近い偶数に丸められます。例えば:

SELECT x,
  round(x::numeric) AS num_round,
  round(x::double precision) AS dbl_round
FROM generate_series(-3.5, 3.5, 1) as x;
  x   | num_round | dbl_round
------+-----------+-----------
 -3.5 |        -4 |        -4
 -2.5 |        -3 |        -2
 -1.5 |        -2 |        -2
 -0.5 |        -1 |        -0
  0.5 |         1 |         0
  1.5 |         2 |         2
  2.5 |         3 |         2
  3.5 |         4 |         4
3
Alex

簡単な解決策はあなたの価値を投げることです:

SELECT round(some_float_column::numeric, 2);

結果はデー​​タ型 numeric です。もう一度キャストする必要がある場合:

SELECT round(some_float_column::numeric, 2)::float8;

必要に応じて、説明したようなラッパー関数を作成します。 PostgreSQLは function overloading をサポートしています。 決してカタログをハックしない-根拠については@Matthewの回答を参照してください。

1