Ao portar um aplicativo para PostgreSQL (9.1), descobri uma estranha incompatibilidade SQL que diz respeito à round()
função, especificamente a versão que recebe um segundo argumento indicando a precisão do arredondamento.
No MySQL, round(some_float_column, 2)
funciona conforme o esperado, retornando o valor de some_float_column
arredondado para duas casas decimais. No Postgres, ele comete erros ERROR: function round(double precision, integer) does not exist
e sugere arquivos HINT: No function matches the given name and argument types. You might need to add explicit type casts.
.
Conforme indicado pelos documentos...
http://www.postgresql.org/docs/9.1/static/functions-math.html
...Postgres tem duas funções de rodada, round(value)
que levam uma precisão dupla ou numérica, e round(value, precision)
que levam um numérico apenas e um número inteiro.
Portanto, não entendo por que a forma de rodada de dois argumentos não leva um duplo para começar, mas tanto faz. Na pesquisa, descobri duas soluções para esse problema. Uma é simplesmente criar minha própria versão round(value, precision)
que leva (double, int) e envolve a versão existente (numérica, int) com uma conversão explícita. Isso certamente funciona, mas eu não gosto disso (minha experiência é em Oracle, que nem mesmo tem um tipo de ponto flutuante verdadeiro). Parece-me que float/double deve ser implicitamente convertido em numérico. E acontece que uma conversão de ASSIGNMENT para esses tipos vem pré-definida. Mas ASSIGNMENT não funciona para chamadas de função, como vemos aqui, precisaria ser IMPLICIT. O problema com issoé que apenas uma conversão pode ser definida por par de tipos, e as conversões de atribuição float4->numeric e float8->numeric são exigidas pelo sistema e não podem ser descartadas. Portanto, a única maneira de tornar essas conversões implícitas é update pg_cast set castcontext = 'i' where castsource in (700,701) and casttarget = 1700
.
Agora estamos hackeando o catálogo, e isso é um sinal de que isso pode ser uma má ideia. Mas não tenho nenhuma evidência concreta de que seja ruim. Os valores float/double são inexatos e os valores numéricos são exatos, portanto, parece-me que a conversão do primeiro para o último seria totalmente preservadora de dados e, portanto, logicamente segura. O único problema potencial que conheço seria introduzir ambiguidade nos padrões de argumento de outras funções, mas o objetivo de fazer essa pergunta é principalmente pescar problemas potenciais que não conheço.
Então, é perigoso mudar o comportamento de conversão float->numeric de ASSIGNMENT para IMPLICIT?
Esteja ciente de que diferentes algoritmos de arredondamento são usados para diferentes tipos de dados.
https://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-NUMERIC-TABLE
Sim, invadir o catálogo é ruim. O motivo nº 1 é que, se você atualizar para a nova versão e esquecer de mover o hack, as coisas começam a quebrar. Apenas executar pg_dump e carregar para a mesma versão em outra instância também fará com que o hack seja perdido. Também há sempre a chance de que uma nova versão do Postgres mude tanto que seu hack agora não seja possível e o force a voltar e refazer a engenharia.
Substituir por sua própria função é o caminho correto a seguir.
A solução simples seria lançar seu valor:
O resultado é do tipo
numeric
de dados, é claro. Transmita novamente, se precisar:Se necessário, crie uma função wrapper como você descreveu. PostgreSQL suporta sobrecarga de função . Nunca hackeie o catálogo para isso - veja a resposta de @ Matthew para a justificativa.