web-dev-qa-db-fra.com

Changer de flotteur-> Chiffre numérique de l'affectation à Implicity, dangereux?

Dans le portage d'une application à PostgreSQL (9.1), une incompatibilité étrange SQL que j'ai découverte concerne la fonction round(), spécifiquement la version qui prend un deuxième argument indiquant la précision de l'arrondissement.

Dans MySQL, round(some_float_column, 2) fonctionne comme prévu, renvoyant la valeur de some_float_column Arrondi à deux décimales. Dans les erreurs informatiques postgres avec ERROR: function round(double precision, integer) does not exist et suggère HINT: No function matches the given name and argument types. You might need to add explicit type casts..

Comme indiqué par les docs ... http://www.postgresql.org/docs/9.1/static/fonctions-math.html ... Postgres a deux fonctions rondes, round(value) qui prend une double précision ou un numérique et round(value, precision) qui prend un numérique uniquement et un entier.

Donc, je ne comprends pas pourquoi la forme de tour de deux arguments ne prend pas un double pour commencer, mais peu importe. Dans la recherche, j'ai découvert deux solutions à ce problème. On consiste simplement à créer ma propre version de round(value, precision) qui prend (double, int) et enveloppe la version existante (numérique, int) avec une distribution explicite. Cela fonctionne certainement, mais je n'aime pas cela (mon arrière-plan est dans Oracle qui n'a même pas de vrai type de point flottant). Il me semble que le flotteur/double devrait être implicitement istable à numérique. Et il s'avère qu'une mission d'affectation pour ces types est prédéfinie. Mais l'affectation ne fonctionne pas pour les appels de fonction, comme nous le voyons ici, il aurait besoin d'être implicite. Le problème avec qui est que une seule coulée peut être définie par paire de types, et les moulures à flotteur à float4-> numériques et float8-> sont requises par le système et peuvent ' t être abandonné. Donc, le seul moyen de rendre ces cases implicites est de update pg_cast set castcontext = 'i' where castsource in (700,701) and casttarget = 1700.

Maintenant, nous sommes en train de pirater le catalogue, et c'est un signe que cela pourrait être une mauvaise idée. Mais je n'ai aucune preuve difficile que c'est mauvais. Float/Double valeurs sont inexactus et les valeurs numériques sont exactes. Il me semble donc que la coulée de l'ancienne à ce dernier serait entièrement préservant les données et donc logiquement sûr. Le seul problème potentiel que je connaisse consistait à introduire de l'ambiguïté dans les modes d'argumentation d'autres fonctions, mais le but de poser cette question est principalement de pêcher pour des problèmes potentiels i ne le font pas .

Donc, est-ce dangereux de changer le comportement de coulée flottant -> de la mission à l'affectation à l'implicite?

6
Noah Yetter

Oui, le piratage dans le catalogue est mauvais. Raison n ° 1 est que si vous mettez à niveau vers une nouvelle version et oubliez de déplacer le hack, les choses commencent à briser. Il suffit d'exécuter pg_dump et le chargement à la même version sur une autre instance perdra également le hack. Il y a aussi toujours la chance qu'une nouvelle version de Postgres change tellement que votre hack est maintenant pas possible et vous oblige à revenir en arrière et à re-ingénieur.

La bonne voie est correcte avec votre propre fonction.

3
Matthew Wood

Veuillez noter que différents algorithmes d'arrondi sont utilisés pour différents types de données.

https://www.postgresql.org/docs/current/static/dataType-numeric.html#daTaType-numérique-table

Lors de l'arrondissement des valeurs, le type numérique arrondit de zéro, tandis que (sur la plupart des machines) Les types réels et doubles de précision ronds des liens ronds au nombre même le plus proche. Par exemple:

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

La solution simple serait de lancer votre valeur:

SELECT round(some_float_column::numeric, 2);

Le résultat est de type de données numeric Bien sûr. Jeter à nouveau, si vous avez besoin de:

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

Si vous devez, créez une fonction d'emballage comme vous décrivez. PostgreSQL prend en charge surcharge de fonction . Jamais pirater le catalogue pour cela - voir la réponse de @ Matthew pour la justification.

1
Erwin Brandstetter