258

Ruby gem 'sequel' 経由で PostgreSQL を使用しています。

小数点以下2桁に丸めようとしています。

これが私のコードです:

SELECT ROUND(AVG(some_column),2)    
FROM table

次のエラーが表示されます。

PG::Error: ERROR:  function round(double precision, integer) does 
not exist (Sequel::DatabaseError)

次のコードを実行してもエラーは発生しません。

SELECT ROUND(AVG(some_column))
FROM table

私が間違っていることを誰かが知っていますか?

4

8 に答える 8

360

PostgreSQL では定義されていませんround(double precision, integer)@Mike Sherrill 'Cat Recall'がコメントで説明している理由により、精度を取る round のバージョンは でのみ使用できますnumeric

regress=> SELECT round( float8 '3.1415927', 2 );
ERROR:  function round(double precision, integer) does not exist

regress=> \df *round*
                           List of functions
   Schema   |  Name  | Result data type | Argument data types |  Type  
------------+--------+------------------+---------------------+--------
 pg_catalog | dround | double precision | double precision    | normal
 pg_catalog | round  | double precision | double precision    | normal
 pg_catalog | round  | numeric          | numeric             | normal
 pg_catalog | round  | numeric          | numeric, integer    | normal
(4 rows)

regress=> SELECT round( CAST(float8 '3.1415927' as numeric), 2);
 round 
-------
  3.14
(1 row)

float8(上記では、は単なる省略形のエイリアスであることに注意してdouble precisionください。PostgreSQL が出力で展開していることがわかります)。

numericの 2 引数形式を使用するには、丸められる値をキャストする必要がありますround::numericのように省略形のキャストを追加するだけround(val::numeric,2)です。


ユーザーに表示するためにフォーマットする場合は、使用しないでくださいround。使用to_char(マニュアルのデータ型フォーマット関数textを参照) を使用すると、フォーマットを指定でき、クライアント言語が値に対して行う可能性のある奇妙さに影響されない結果が得られnumericます。例えば:

regress=> SELECT to_char(float8 '3.1415927', 'FM999999999.00');
    to_char    
---------------
 3.14
(1 row)

to_charは、書式設定の一部として数値を四捨五入します。FMプレフィックスは、to_char先頭のスペースでパディングしたくないことを示しています。

于 2012-10-28T22:25:30.680 に答える
123

        ((これは Wiki です!編集して強化してください!))

キャストの古い構文も試してください。

SELECT ROUND( AVG(some_column)::numeric, 2 ) FROM table;

PostgreSQL のどのバージョンでも動作します。
...しかし、決定的な解決策として、ROUND 関数をオーバーロードすることができます。

キャスト戦略としてのオーバーロード

CREATE FUNCTION ROUND(float,int) RETURNS NUMERIC AS $f$
  SELECT ROUND( CAST($1 AS numeric), $2 )
$f$ language SQL IMMUTABLE;

これで、命令が正常に機能するようになりました。次の完全な比較を試してください。

SELECT trunc(n,3), round(n,3) n_round, round(f,3) f_round,
       pg_typeof(n) n_type, pg_typeof(f) f_type, pg_typeof(round(f,3)) f_round_type
FROM (SELECT 2.0/3.0, 2/3::float) t(n,f);
トランク n_round f_round n_type f_type f_round_type
0.666 0.667 0.667 数値 倍精度 数値

ROUND(float,int)関数は、(10 進数) NUMERIC データ型を返します。f_roundこれは、一部のアプリケーションでは問題ありません: 問題が解決しました!

別のアプリケーションでは、結果としてフロートも必要です。別の方法は、関数を使用round(f,3)::float または作成するround_tofloat()ことです。他の代替手段として、ROUND関数を再度オーバーロードし、浮動小数点数の精度と精度のすべての範囲を使用して、精度が定義されているときに浮動小数点数を返すことです ( IanKenney の回答を参照)。

CREATE FUNCTION ROUND(
  input float,     -- the input number
  accuracy float   -- accuracy, the "counting unit"
) RETURNS float AS $f$
   SELECT ROUND($1/accuracy)*accuracy
$f$ language SQL IMMUTABLE;

試す

  SELECT round(21.04, 0.05);     -- 21.05 float!
  SELECT round(21.04, 5::float); -- 20
  SELECT round(1/3., 0.0001);    -- 0.3333
  SELECT round(2.8+1/3., 0.5);   -- 3.15
  SELECT round(pi(), 0.0001);    -- 3.1416

PS: オーバーロード後のコマンド\df roundは、psql 次の表のように表示されます

スキーマ | 名前 | 結果 | 口論  
------------+---------------------+---------------------+------------------
 マイスキーマ | ラウンド | 数値 | フロート、整数
 マイスキーマ | ラウンド | フロート | 浮く、浮く
 pg_catalog | ラウンド | フロート | 浮く            
 pg_catalog | ラウンド | 数値 | 数値   
 pg_catalog | ラウンド | 数値 | 数値、整数          

ここで、floatはスキーマを使用しない場合のmyschemadouble precisionと同義です。関数はデフォルトのものです。Guide the build in math functions を参照してくださいpublicpg_catalog

丸めと整形

この関数はラウンドto_charプロシージャを内部的に適用するため、端末に最終結果を表示することだけが目的の場合は、修飾子を数値形式パターンのプレフィックスとして使用できます。FM

SELECT round(x::numeric,2), trunc(x::numeric,2), to_char(x, 'FM99.99')
FROM (SELECT 2.0/3) t(x);
円形 トランク to_char
0.67 0.66 .67

ノート

問題の原因

一部の PostgreSQL 関数にオーバーロードの欠如があります。なぜ (???): 「それは欠如である」(!) と思いますが、@CraigRinger、@Catcall および PostgreSQL チームは「pg の歴史的根拠」について同意しています。

性能と再利用に関する注意

pg_catalog の ROUND などの組み込み関数は、直接キャスト エンコーディングと比較して、パフォーマンスを損なうことなくオーバーロードできます。高いパフォーマンスを得るためにユーザー定義のキャスト関数を実装する場合は、次の 2 つの予防措置を講じる必要があります。

  • このIMMUTABLE節は、このようなコード スニペットにとって非常に重要です。これは、ガイドで述べられているように、「クエリが定数引数で関数を呼び出すときに、オプティマイザが関数を事前評価できるようにする」ためです。

  • 「純粋な SQL」を除いて、PLpgSQL が優先言語です。JIT 最適化(および場合によっては並列処理) ではlanguage SQL、より適切な最適化が得られます。関数呼び出しを使用する代わりに、小さなコードをコピーして貼り付けるようなものです。

結論:ROUND(float,int)最適化後の上記の関数は、@CraigRinger の回答よりも高速です。(正確に)同じ内部表現にコンパイルされます。したがって、PostgreSQL の標準ではありませんが、 pg_pubLibのような集中化された再利用可能な「スニペットのライブラリ」によって、プロジェクトの標準にすることができます 。


n 番目のビットまたはその他の数値表現に丸めます

float は 2 進表現であり、ビット数またはその 16 進表現を丸める必要があるため、PostgreSQL が float データ型の数を丸めるのは意味がないと主張する人もいます。

さて、風変わりな提案を追加して、問題を解決しましょう... ここでの目的は、オーバーロードされた別の関数で float 型を返すことです
  ROUND(float, text, int) RETURNS float
text

  • 'dec'「小数表現」の場合、
  • 'bin'「バイナリ」表現と
  • 'hex'16 進表現用。

したがって、異なる表現では、丸められる桁数について異なる解釈があります。dが 10 進数または 16 進数の代わりに 2 進数をカウントしている場合、「小数桁」(元のd桁よりも少ない) を使用して、より短い近似値で数値xを丸めることは短くなります。

C++ なしで「純粋な SQL」を使用するのは簡単ではありませんが、このコード スニペットは説明し、回避策として使用できます。

-- Looking for a round_bin() function! this is only a workaround:
CREATE FUNCTION trunc_bin(x bigint, t int) RETURNS bigint AS $f$
    SELECT ((x::bit(64) >> t) << t)::bigint;
$f$ language SQL IMMUTABLE;
 
CREATE FUNCTION ROUND(
   x float, 
   xtype text,  -- 'bin', 'dec' or 'hex'
   xdigits int DEFAULT 0
) 
RETURNS FLOAT AS $f$
    SELECT CASE
       WHEN xtype NOT IN ('dec','bin','hex') THEN 'NaN'::float
       WHEN xdigits=0 THEN ROUND(x)
       WHEN xtype='dec' THEN ROUND(x::numeric,xdigits)
       ELSE (s1 ||'.'|| s2)::float
      END
    FROM (
      SELECT s1,
             lpad( 
               trunc_bin( s2::bigint, CASE WHEN xd<bin_bits THEN bin_bits - xd ELSE 0 END )::text,
               l2,
               '0'
             ) AS s2
      FROM (
        SELECT *, 
             (floor( log(2,s2::numeric) ) +1)::int AS bin_bits, -- most significant bit position
             CASE WHEN xtype='hex' THEN xdigits*4 ELSE xdigits END AS xd
        FROM (
          SELECT s[1] AS s1, s[2] AS s2, length(s[2]) AS l2
          FROM (SELECT regexp_split_to_array(x::text,'\.')) t1a(s)
        ) t1b
      ) t1c
    ) t2
$f$ language SQL IMMUTABLE;

試す

 SELECT round(1/3.,'dec',4);     -- 0.3333 float!
 SELECT round(2.8+1/3.,'dec',1); -- 3.1 float!
 SELECT round(2.8+1/3.,'dec');   -- ERROR, need to cast string 
 SELECT round(2.8+1/3.,'dec'::text); -- 3 float
 SELECT round(2.8+1/3.,'dec',0); -- 3 float

 SELECT round(2.8+1/3.,'hex',0); -- 3 float (no change)
 SELECT round(2.8+1/3.,'hex',1); -- 3.1266
 SELECT round(2.8+1/3.,'hex',3); -- 3.13331578486784

 SELECT round(2.8+1/3.,'bin',1);  -- 3.1125899906842625
 SELECT round(2.8+1/3.,'bin',6);  -- 3.1301821767286784
 SELECT round(2.8+1/3.,'bin',12); -- 3.13331578486784

また、次の\df roundものがあります。

 Schema     |  Name | Result  | Argument  
------------+-------+---------+---------------
 myschema   | round | float   | x float, xtype text, xdigits int DEFAULT 0

于 2014-01-05T13:28:45.203 に答える
9
SELECT ROUND(SUM(amount)::numeric, 2) AS total_amount
FROM transactions

与えます: 200234.08

于 2020-05-21T08:45:21.977 に答える
4

ブライアンの回答によると、これを行うと、クエリの小数点以下を制限できます。km/h から m/s に変換して dygraphs に表示しますが、dygraphs で表示すると変に見えました。代わりにクエリで計算を行うとうまく見えます。これはpostgresql 9.5.1にあります。

select date,(wind_speed/3.6)::numeric(7,1) from readings;
于 2016-02-20T13:56:33.623 に答える
1

エラー: 関数 round(double precision, integer) は存在しません

解決策: addtype キャストが必要です。

元: round(extract(second from job_end_time_t)::integer,0)

于 2015-12-21T10:23:47.287 に答える