8.2. Tipo de dados monetário #

O tipo de dados money armazena uma quantia monetária com precisão fracionária fixa; veja a Tabela 8.3. A precisão fracionária é determinada pela configuração do parâmetro lc_monetary no servidor de banco de dados. O intervalo mostrado na tabela assume serem dois dígitos fracionários. A entrada é aceita em vários formatos, incluindo literais inteiros e de ponto flutuante, assim como a formatação monetária típica, como '$1.000,00'. A saída geralmente está nessa última forma, mas depende da localidade.

Tabela 8.3. Tipo monetário

NomeTamanho de armazenamentoDescriçãoIntervalo dos valores
money8 bytesquantia monetária-92233720368547758.08 a +92233720368547758.07

Como a saída desse tipo de dados é sensível à localidade, carregar dados do tipo de dados money em um banco de dados que tenha uma configuração diferente de lc_monetary pode não funcionar. Para evitar problemas, antes de restaurar um dump em um novo banco de dados, certifique-se de que lc_monetary tenha o mesmo valor, ou um valor equivalente, do banco de dados de onde foi feito o dump.

Valores dos tipos de dados numeric, int e bigint podem ser convertidos diretamente para money. A conversão dos tipos de dados real e double precision podem ser feitas convertendo para numeric primeiro, por exemplo:

SELECT '12.34'::float8::numeric::money;

Contudo, isto não é recomendado. Os números de ponto flutuante não devem ser usados para lidar com dinheiro, devido ao erro possível de arredondamento.

O tipo de dados money pode ser convertido para numeric sem perda de precisão. A conversão para outros tipos de dados pode perder precisão e, também, deve ser feita em duas etapas:

SELECT '52093.89'::money::numeric::float8;

A divisão de um valor money por um valor inteiro é realizada com truncamento da parte fracionária em direção a zero. Para obter um resultado arredondado, deve-se dividir por um valor de ponto flutuante ou converter o valor money para numeric antes de dividir e voltar para money depois. (Este último é preferível para evitar o risco de perda de precisão.) Quando um valor do tipo de dados money é dividido por outro valor do tipo de dados money, o resultado é de precisão dupla (ou seja, um número puro, não dinheiro); as unidades monetárias se cancelam na divisão.

Exemplo 8.3. Exemplo do tradutor

Soma de dois valores do tipo de dados money, executado no Debian 12 com locale = LC_MONETARY=pt_BR.UTF-8.

SELECT  name, setting
    FROM pg_settings WHERE name='lc_monetary';
    name     |   setting
-------------+-------------
 lc_monetary | pt_BR.UTF-8
(1 linha)
CREATE OR REPLACE FUNCTION soma_monetária(op1 MONEY, op2 MONEY)
    RETURNS MONEY AS $$
-- Somar dois números do tipo de dados money
-- @param op1 tipo de dados money - primeiro operando
-- @param op2 tipo de dados money - segundo operando
-- @return soma de op1 com op2
BEGIN
    RETURN op1 + op2;
END;
$$ LANGUAGE plpgsql;

SELECT soma_monetária('1.234,56'::money, 1000::money);
 soma_monetária
----------------
    R$ 2.234,56
(1 linha)