Comment arrondir une moyenne à 2 décimales dans PostgreSQL?

206

J'utilise PostgreSQL via la "suite" de Ruby gem.

J'essaie d'arrondir à deux décimales.

Voici mon code:

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

J'obtiens l'erreur suivante:

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

Je n'obtiens aucune erreur lorsque j'exécute le code suivant:

SELECT ROUND(AVG(some_column))
FROM table

Quelqu'un sait-il ce que je fais mal?

user1626730
la source
3
Votre message d'erreur ne correspond pas au code de votre question.
mu est trop court
L'erreur de syntaxe mise à part, cette question étroitement liée sur dba.SE jette un éclairage sur l'arrondissement des nombres à double précision dans PostgreSQL.
Erwin Brandstetter
@muistooshort, Merci de l'avoir signalé. Il devrait dire «rond» là où il est dit «avg». Édité.
user1626730
pour la recherche des résultats, j'obtiens également cet indice en sortie de l'invite:HINT: No function matches the given name and argument types. You might need to add explicit type casts.
Vzzarr

Réponses:

290

PostgreSQL ne définit pas round(double precision, integer). Pour des raisons expliquées par @Mike Sherrill 'Cat Recall' dans les commentaires, la version de round qui prend une précision n'est disponible que pour 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)

(Dans ce qui précède, notez qu'il float8s'agit simplement d'un alias abrégé pour double precision. Vous pouvez voir que PostgreSQL l'étend dans la sortie).

Vous devez convertir la valeur à arrondir numericpour utiliser la forme à deux arguments de round. Ajoutez juste ::numericpour la distribution de raccourci, comme round(val::numeric,2).


Si vous formatez pour l'affichage à l'utilisateur, n'utilisez pas round. Utilisez to_char(voir: fonctions de formatage des types de données dans le manuel), qui vous permet de spécifier un format et vous donne un textrésultat qui n'est pas affecté par les bizarreries que votre langue client pourrait faire avec les numericvaleurs. Par exemple:

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

to_chararrondira les nombres pour vous dans le cadre du formatage. Le FMpréfixe indique to_charque vous ne voulez pas de remplissage avec des espaces de début.

Craig Ringer
la source
Hmm. Quand j'essaye ROUND(CAST(FLOAT8 '3.1415927' AS NUMERIC),2);, j'obtiens «0.314E1». Et j'ai mon code écrit ROUND(AVG(val),2)encore obtenir l'erreur que j'ai décrite dans ma question.
user1626730
Je viens de courir ROUND(CAST(FLOAT8 '3.1415927' AS NUMERIC),2);sur PgAdmin et Ruby. Avec PgAdmin, j'obtiens 3.14, mais avec Ruby (en utilisant la gemme Sequel) j'obtiens '0.314E1'. Je me demande pourquoi c'est ...
user1626730
13
«Pour une raison étrange, la version de round qui prend une précision n'est disponible que pour les nombres. Les nombres à virgule flottante sont des "approximations utiles". Si vous demandez au code d'arrondir un nombre à virgule flottante à deux décimales, retournant un autre nombre à virgule flottante, il n'y a aucune garantie que l'approximation la plus proche de la «bonne» réponse n'aura que deux chiffres à droite de la décimale. Les nombres sont effectivement des entiers mis à l'échelle; ils n'ont pas ce problème.
Mike Sherrill 'Cat Recall'
@Catcall Bon point - une doubleversion de roundaurait besoin de retourner numericou (ugh) text, donc elle pourrait aussi bien prendre un numericargument.
Craig Ringer
6
Pour ceux qui essaient de trouver le commentaire de @Catcall: maintenant c'est Mike Sherrill 'Cat Recall'
18446744073709551615
95

Essayez aussi l'ancienne syntaxe pour la diffusion,

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

fonctionne avec n'importe quelle version de PostgreSQL.

Il y a un manque de surcharge dans certaines fonctions PostgreSQL, pourquoi (???): Je pense que "c'est un manque" (!), Mais @CraigRinger, @Catcall et l'équipe de PostgreSQL sont d'accord sur "la logique historique de pg".

PS: un autre point sur l'arrondi est la précision , vérifiez la réponse de @ IanKenney .


Surcharge comme stratégie de casting

Vous pouvez surcharger la fonction ROUND avec,

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

Maintenant, votre instruction fonctionnera correctement, essayez (après la création de la fonction)

 SELECT round(1/3.,4); -- 0.3333 numeric

mais il retourne un type NUMERIC ... Pour conserver la première surcharge d'usage commun, on peut retourner un type FLOAT lorsqu'un paramètre TEXT est proposé,

 CREATE FUNCTION ROUND(float, text, int DEFAULT 0) 
 RETURNS FLOAT AS $$
    SELECT CASE WHEN $2='dec'
                THEN ROUND($1::numeric,$3)::float
                -- ... WHEN $2='hex' THEN ... WHEN $2='bin' THEN... complete!
                ELSE 'NaN'::float  -- like an error message 
            END;
 $$ language SQL IMMUTABLE;

Essayer

 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'::text); -- need to cast string? pg bug 

PS: vérifier \df roundaprès les surcharges, affichera quelque chose comme,

Schéma | Nom | Type de données de résultat | Types de données d'argument
------------ + ------- + ------------------ + ---------- ------------------
 myschema | rond | double précision | double précision, texte, int
 myschema | rond | numérique | double précision, int
 pg_catalog | rond | double précision | double precision            
 pg_catalog | rond | numérique | numérique   
 pg_catalog | rond | numérique | numérique, entier          

Les pg_catalogfonctions sont celles par défaut, voir le manuel des fonctions mathématiques intégrées .

Peter Krauss
la source
41

Essayez avec ceci:

SELECT to_char (2/3::float, 'FM999999990.00');
-- RESULT: 0.67

Ou simplement:

SELECT round (2/3::DECIMAL, 2)::TEXT
-- RESULT: 0.67
Atiruz
la source
5
Je trouve qu’il s’agit là d’une démarche beaucoup plus concise et plus précise avec ma réponse quotidienne à cette question. : arc:
craastad
2
Pareil ici! Solution très courte et utile.
Alexey Shabramov
9

vous pouvez utiliser la fonction ci-dessous

 SELECT TRUNC(14.568,2);

le résultat montrera:

14.56

vous pouvez également convertir votre variable dans le type souhaité:

 SELECT TRUNC(YOUR_VAR::numeric,2)
AdagioDev
la source
5

Essayez de convertir votre colonne en une valeur numérique comme:

SELECT ROUND(cast(some_column as numeric),2) FROM table
Gabriel Jaime Sierra Rua
la source
travailler dans pg10 sans lancer avec des valeurs numériques.
Sergio Belevskij
3

Selon la réponse de Bryan, vous pouvez le faire pour limiter les décimales dans une requête. Je convertis de km / h en m / s et je l'affiche dans des dygraphes, mais quand je l'ai fait dans des dygraphes, cela avait l'air bizarre. Cela a l'air bien lors du calcul dans la requête. C'est sur postgresql 9.5.1.

select date,(wind_speed/3.6)::numeric(7,1) from readings;
kometen
la source
1

Erreur: la fonction round (double précision, entier) n'existe pas

Solution : vous devez ajouter le type de cast, cela fonctionnera

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

user5702982
la source
0

sélectionnez ROUND (SUM (amount) :: numeric, 2) comme total_amount FROM transactions

donne: 200234.08

vlatko606
la source