8.1. Tipos de dados numérico #

8.1.1. Tipos de dados inteiros
8.1.2. Tipos de dados de número de precisão arbitrária
8.1.3. Tipos de dados de ponto flutuante
8.1.4. Tipos de dados seriais

Os tipos de dados numéricos consistem em inteiros de dois, quatro e oito bytes, números de ponto flutuante de quatro e oito bytes, e decimais de precisão selecionável. A Tabela 8.2 lista os tipos de dados disponíveis.

Tabela 8.2. Tipos de dados numéricos

NomeTamanho de armazenamentoDescriçãoIntervalo dos valores
smallint2 bytesinteiro com intervalo de valores pequeno-32768 a +32767
integer4 bytesescolha usual para inteiro-2147483648 a +2147483647
bigint8 bytesinteiro com intervalo de valores grande-9223372036854775808 a +9223372036854775807
decimalvariávelprecisão especificada pelo usuário, exatoaté 131072 dígitos antes do ponto decimal; até 16383 dígitos após o ponto decimal
numericvariávelprecisão especificada pelo usuário, exatoaté 131072 dígitos antes do ponto decimal; até 16383 dígitos após o ponto decimal
real4 bytesprecisão variável, inexatoprecisão de 6 dígitos decimais
double precision8 bytesprecisão variável, inexatoprecisão de 15 dígitos decimais
smallserial2 bytesinteiro pequeno com autoincremento1 a 32767
serial4 bytesinteiro com autoincremento1 a 2147483647
bigserial8 bytesinteiro grande com autoincremento1 a 9223372036854775807

A sintaxe das constantes para os tipos de dados numéricos é descrita na Seção 4.1.2. Os tipos de dados numéricos possuem um conjunto completo de operadores e funções aritméticas correspondentes. Veja o Capítulo 9 para obter mais informações. As próximas seções descrevem os tipos de dados numéricos em detalhe.

8.1.1. Tipos de dados inteiros #

Os tipos de dados smallint, integer e bigint armazenam números inteiros, ou seja, números sem a parte fracionária, com intervalos diferentes. A tentativa de armazenar um valor fora do intervalo permitido resulta em erro.

O tipo de dados integer é a escolha usual, porque oferece o melhor equilíbrio entre intervalo de valores, tamanho de armazenamento e desempenho. Geralmente o tipo de dados smallint só é usado quando o espaço em disco está muito escasso. O tipo de dados bigint é projetado para ser usado quando o intervalo de valores de integer não for suficiente.

O padrão SQL só especifica os tipos de dados inteiros integer (ou int), smallint e bigint. Os nomes de tipo de dados int2, int4 e int8 são extensões, que também são usadas por outros sistemas de banco de dados SQL.

8.1.2. Tipos de dados de número de precisão arbitrária #

O tipo de dados numeric pode armazenar números com um número muito grande de dígitos. É particularmente recomendado para armazenar valores monetários e outras quantidades onde a exatidão é necessária. Cálculos com valores numeric produzem resultados exatos sempre que possível, por exemplo, adição, subtração e multiplicação. Entretanto, os cálculos usando valores numeric são muito lentos em comparação com os tipos de dados inteiros ou com os tipos de dados de ponto flutuante descritos na próxima seção.

São usados os seguintes termos abaixo: A precisão do tipo de dados numeric é a contagem dos dígitos significativos no número todo, ou seja, o número de dígitos presentes em ambos os lados do ponto decimal. A escala de um numeric é a contagem dos dígitos decimais na parte fracionária, à direita do ponto decimal. Assim, o número 23.5141 tem uma precisão de 6 dígitos e uma escala de 4 dígitos. Os números inteiros podem ser considerados tendo uma escala de zero dígitos.

Podem ser especificadas tanto a precisão máxima quanto a escala máxima de uma coluna numeric. Para declarar uma coluna do tipo de dados numeric é usada a sintaxe:

NUMERIC(precisão, escala)

A precisão deve ser positiva, enquanto a escala pode ser positiva ou negativa (veja abaixo). Como alternativa

NUMERIC(precisão)

seleciona uma escala de 0. Especificar

NUMERIC

sem precisão ou escala cria uma coluna numérica irrestrita, na qual valores numéricos de qualquer comprimento podem ser armazenados, até os limites da implementação. Uma coluna desse tipo de dados não forçará os valores de entrada para nenhuma escala específica, enquanto as colunas numeric com uma escala declarada vão forçar os valores de entrada para esta escala. (O padrão SQL requer uma escala padrão de 0, ou seja, coerção para precisão inteira. Achamos isto um pouco inútil. Se estiver preocupado com a portabilidade, sempre especifique a precisão e a escala explicitamente.)

Nota

A precisão máxima que pode ser especificada explicitamente em uma declaração do tipo de dados NUMERIC é 1000. Uma coluna NUMERIC irrestrita está sujeita aos limites descritos na Tabela 8.2.

Se a escala de um valor a ser armazenado for maior que a escala declarada da coluna, o sistema irá arredondar o valor para o número especificado de dígitos fracionários. Então, se o número de dígitos à esquerda da vírgula decimal exceder a precisão declarada menos a escala declarada, irá ocorrer um erro. Por exemplo, uma coluna declarada como

NUMERIC(3, 1)

irá arredondar os valores para 1 casa decimal e poderá armazenar valores entre -99,9 e 99,9, inclusive.

A partir do PostgreSQL 15, é permitido declarar uma coluna do tipo de dados numeric com escala negativa. Assim, os valores serão arredondados para a esquerda da vírgula decimal. A precisão ainda representa o número máximo de dígitos não arredondados. Portanto, uma coluna declarada como

NUMERIC(2, -3)

irá arredondar os valores para o milhar mais próximo e poderá armazenar valores entre -99000 e 99000, inclusive. Também é permitido declarar uma escala maior do que a precisão declarada. Esta coluna só pode conter valores fracionários e exige que o número de dígitos zero imediatamente à direita da vírgula decimal seja pelo menos a escala declarada menos a precisão declarada. Por exemplo, uma coluna declarada como

NUMERIC(3, 5)

irá arredondar os valores para 5 casas decimais e poderá armazenar valores entre -0,00999 e 0,00999, inclusive.

Nota

O PostgreSQL permite que a escala em uma declaração de tipo de dados numeric seja qualquer valor no intervalo de -1000 a 1000. Entretanto, o padrão SQL exige que a escala esteja no intervalo de 0 a precisão. Utilizar escalas fora deste intervalo pode não ser compatível com outros sistemas de banco de dados.

Os valores numéricos são fisicamente armazenados sem zeros extras à esquerda ou à direita. Assim, a precisão declarada e a escala de uma coluna são as máximas, e não alocações fixas. (Nesse sentido, o tipo de dados numeric é mais semelhante a varchar(n) do que a char(n).) O requisito real de armazenamento é de dois bytes para cada grupo de quatro dígitos decimais, mais três a oito bytes de sobrecarga.

Além dos valores numéricos comuns, o tipo de dados numeric possui os valores especiais

Infinity
-Infinity
NaN

que foram adaptados do padrão IEEE 754, e representam infinito, menos infinito e não é um número, respectivamente. Ao se escrever estes valores como constantes em um comando SQL, devem ser colocados apóstrofos ao redor deles, por exemplo, UPDATE table SET x = '-Infinity'. Na entrada, estas cadeias de caracteres são reconhecidas sem ser feita distinção entre letras maiúsculas e minúsculas. Os valores infinitos podem, como alternativa, serem escritos como inf e -inf.

Exemplo 8.1. Exemplo do tradutor

Neste exemplo é criada uma expressão de tabela contendo os valores numéricos '-Infinity', 'Infinity' e 'NaN', em seguida é feita uma consulta para mostrar a saída destes valores.

WITH n("Menos Infinito", "Mais Infinito", "Não é um número") AS
    (VALUES( '-Infinity'::numeric, 'Infinity'::numeric, 'NaN'::numeric))
SELECT * FROM n;
 Menos Infinito | Mais Infinito | Não é um número
----------------+---------------+-----------------
      -Infinity |      Infinity |             NaN
(1 linha)

Os valores infinitos se comportam segundo as expectativas da matemática. Por exemplo, Infinity mais qualquer valor finito é igual a Infinity, assim como Infinity mais Infinity; mas Infinity menos Infinity resulta em NaN (não é um número), porque não tem uma interpretação bem definida. Note que infinito só pode ser armazenado em uma coluna numeric irrestrita, porque notacionalmente excede qualquer limite de precisão finita.

Exemplo 8.2. Exemplo do tradutor

Neste exemplo 'Infinity' é subtraído de 'Infinity' produzindo 'NaN'.

CREATE FUNCTION subtrair(op1 NUMERIC, op2 NUMERIC)
    RETURNS NUMERIC AS $$
-- Calcula a subtração entre dois números do tipo de dados numeric
-- @param op1 tipo de dados numeric - minuendo
-- @param op2 tipo de dados numeric - subtraendo
-- @return diferença (op1 - op2)
BEGIN
    RETURN op1 - op2;
END;
$$ LANGUAGE plpgsql;

SELECT subtrair('Infinity', 'Infinity');
 subtrair
----------
      NaN
(1 linha)

O valor NaN (não é um número) é usado para representar resultados de cálculo indefinidos. Em geral, qualquer operação com uma entrada NaN produz outro NaN. A única exceção é quando as outras entradas da operação são tais que a mesma saída seria obtida se NaN fosse substituído por qualquer valor numérico finito ou infinito; então, este valor de saída é usado para NaN também. (Um exemplo desse princípio é que NaN elevado à potência zero produz um.)

Nota

Na maioria das implementações do conceito não-é-um-número, NaN não é considerado igual a nenhum outro valor numérico (incluindo o próprio NaN). Para permitir que os valores numeric sejam classificados e usados em índices baseados em árvore, o PostgreSQL trata os valores NaN como sendo iguais, e maiores do que todos os outros valores que não são NaN.

Os tipos de dados decimal e numeric são equivalentes. Os dois tipos de dados fazem parte do padrão SQL.

Ao arredondar valores, o tipo de dados numeric arredonda para zero, enquanto (na maioria das máquinas) os tipos de dados real e double precision arredondam para o número mais próximo. Por exemplo:

SELECT x,
  round(x::numeric) AS num_round,
  round(x::double precision) AS dbl_round,
  floor(x::numeric) AS num_floor,
  ceil(x::numeric)  AS num_ceil,
  trunc(x::numeric) AS num_trunc
FROM generate_series(-3.5, 3.5, 1) as x;

  x   | num_round | dbl_round | num_floor | num_ceil | num_trunc
------+-----------+-----------+-----------+----------+-----------
 -3.5 |        -4 |        -4 |        -4 |       -3 |        -3
 -2.5 |        -3 |        -2 |        -3 |       -2 |        -2
 -1.5 |        -2 |        -2 |        -2 |       -1 |        -1
 -0.5 |        -1 |        -0 |        -1 |        0 |         0
  0.5 |         1 |         0 |         0 |        1 |         0
  1.5 |         2 |         2 |         1 |        2 |         1
  2.5 |         3 |         2 |         2 |        3 |         2
  3.5 |         4 |         4 |         3 |        4 |         3
(8 linhas)
 

Nota do tradutor

As colunas mostrando as saídas das funções floor, ceil e trunc foram adicionadas ao exemplo original, para mostrar que o arredondamento no tipo de dados numeric não corresponde, em todos os casos, aos resultados produzidos pela função floor, que retorna o inteiro mais próximo menor ou igual ao argumento, nem aos resultados produzidos pela função ceil, que retorna o inteiro mais próximo maior ou igual ao argumento, nem, em nenhum caso, aos resultados produzidos pela função trunc, que trunca para inteiro (em direção a zero). Veja a Seção 9.3.

8.1.3. Tipos de dados de ponto flutuante #

Os tipos de dados real e double precision são tipos de dados numéricos inexatos de precisão variável. Em todas as plataformas aceitas no momento, estes tipos de dados são implementações do Padrão IEEE 754 para Aritmética de Ponto Flutuante Binária (precisão simples e dupla, respectivamente), na medida em que o processador, sistema operacional e compilador subjacentes aceitam isto.

Inexato significa que alguns valores não podem ser convertidos para o formato interno de forma exata, sendo armazenados como aproximações, de modo que armazenar e recuperar o valor pode apresentar pequenas discrepâncias. Gerenciar estes erros e como eles se propagam por meio de cálculos é assunto de todo um ramo da matemática e da ciência da computação e não será discutido aqui, exceto pelos seguintes pontos:

  • Se precisar de armazenamento e cálculos exatos (como para valores monetários), use o tipo de dados numeric.

  • Se quiser fazer cálculos complicados com estes tipos de dados para algo importante, principalmente se depender de determinado comportamento em casos limite (infinity, underflow), a implementação deve ser avaliada com cuidado.

  • A comparação de dois valores de ponto flutuante para igualdade pode nem sempre funcionar conforme o esperado.

Em todas as plataformas aceitas no momento, o tipo de dados real tem um intervalo de cerca de 1E-37 a 1E+37 com uma precisão de pelo menos 6 dígitos decimais. O tipo de dados double precision tem um intervalo de cerca de 1E-307 a 1E+308 com uma precisão de pelo menos 15 dígitos. Valores muito grandes ou muito pequenos causarão erro. Pode ocorrer arredondamento se a precisão do número de entrada for muito alta. Números muito próximos de zero, que não são representáveis como distintos de zero, causarão um erro de underflow. [42]

Por padrão, os valores de ponto flutuante são exibidos em formato de texto em sua representação decimal precisa mais curta; o valor decimal produzido está mais próximo do valor binário armazenado verdadeiro do que de qualquer outro valor representável na mesma precisão binária. (Entretanto, no momento o valor de saída nunca está exatamente no meio do caminho entre dois valores representáveis, a fim de evitar um bug generalizado onde as rotinas de entrada não respeitam adequadamente a regra de arredondar para o valor mais próximo.) Este valor usará no máximo 17 dígitos decimais significativos para valores float8, e no máximo 9 dígitos para valores float4.

Nota

Este formato de saída mais curto e preciso é muito mais rápido de gerar do que o formato arredondado histórico.

Para manter a compatibilidade com a saída gerada por versões mais antigas do PostgreSQL, e para permitir que a precisão da saída seja reduzida, pode ser usado o parâmetro extra_float_digits para selecionar a saída decimal arredondada em vez dessa. Definir o valor 0 restaura o padrão anterior que era arredondar o valor para 6 (para float4) ou 15 (para float8) dígitos decimais significativos. Definir um valor negativo reduz ainda mais o número de dígitos; por exemplo, -2 arredonda a saída para 4 ou 13 dígitos, respectivamente.

Qualquer valor de extra_float_digits maior que 0 seleciona o formato mais curto e preciso.

Nota

Historicamente, as aplicações que queriam valores precisos tinham que definir extra_float_digits como 3 para obtê-los. Para a máxima compatibilidade entre as versões, elas devem continuar a fazê-lo.

Além dos valores numéricos comuns, os tipos de dados de ponto flutuante têm os valores especiais

Infinity
-Infinity
NaN

que representam os valores especiais IEEE 754 infinito, menos infinito e não é um número, respectivamente. Ao se escrever estes valores como constantes em um comando SQL, devem ser colocados apóstrofos ao redor deles, por exemplo, UPDATE table SET x = '-Infinity'. Na entrada, estas cadeias de caracteres são reconhecidas sem ser feita distinção entre letras maiúsculas e minúsculas. Os valores infinitos podem, como alternativa, serem escritos como inf e -inf.

Nota

O padrão IEEE 754 especifica que NaN não deve ser comparado como sendo igual a nenhum outro valor de ponto flutuante (incluindo o próprio NaN). Para permitir que os valores de ponto flutuante sejam classificados e usados em índices baseados em árvore, o PostgreSQL trata os valores NaN como sendo iguais, e maiores do que todos os outros valores que não são NaN.

O PostgreSQL também aceita as notações do padrão SQL float e float(p) para especificar tipos de dados numéricos inexatos. Aqui, p especifica a precisão mínima aceitável em dígitos binários. O PostgreSQL aceita de float(1) a float(24) para selecionar o tipo de dados real, e de float(25) a float(53) para selecionar o tipo de dados double precision. Valores de p fora do intervalo permitido ocasionam erro. float sem precisão especificada significa double precision.

8.1.4. Tipos de dados seriais #

Nota

Esta seção descreve a maneira específica usada pelo PostgreSQL para criar uma coluna com incremento automático. Outra maneira é usar o recurso de coluna de identidade do padrão SQL, descrito na Seção 5.3.

Os tipos de dados smallserial, serial e bigserial não são tipos de dados de verdade, mas apenas uma conveniência notacional para criar identificadores únicos para as colunas (similar à propriedade AUTO_INCREMENT aceita por alguns outros bancos de dados). Na implementação corrente, especificar

CREATE TABLE nome_da_tabela (
    nome_da_coluna SERIAL
);

equivale a especificar

CREATE SEQUENCE nome_da_tabela_nome_da_coluna_seq AS integer;
CREATE TABLE nome_da_tabela (
    nome_da_coluna integer NOT NULL DEFAULT nextval('nome_da_tabela_nome_da_coluna_seq')
);
ALTER SEQUENCE nome_da_tabela_nome_da_coluna_seq OWNED BY nome_da_tabela.nome_da_coluna;

Assim, é criada uma coluna de inteiros e organizada para que seu valor padrão seja atribuído a partir de um gerador de sequência. A restrição NOT NULL é aplicada para garantir que não pode ser inserido o valor nulo. (Geralmente é bom anexar a restrição UNIQUE ou PRIMARY KEY, para evitar que valores duplicados sejam inseridos acidentalmente, mas isto não é automático.) Por último, a sequência é marcada como pertencente à coluna, para ser excluída se a coluna ou a tabela for excluída.

Nota

Como smallserial, serial e bigserial são implementados usando sequências, pode haver "buracos" ou lacunas na sequência de valores que aparece na coluna, mesmo que nenhuma linha tenha sido excluída. Um valor alocado da sequência é considerado usado, mesmo que uma linha contendo este valor nunca tenha sido inserida com êxito na coluna da tabela. Isso pode acontecer, por exemplo, se a transação de inserção for desfeita. Veja nextval() na Seção 9.17 para obter detalhes.

Para inserir o próximo valor da sequência na coluna serial, deve ser especificado que a coluna serial vai receber o seu valor padrão. Isso pode ser feito excluindo a coluna da lista de colunas na instrução INSERT, ou usando a palavra-chave DEFAULT.

Os nomes de tipo de dados serial e serial4 são equivalentes e ambos criam colunas integer. Os nomes de tipo de dados bigserial e serial8 funcionam da mesma maneira, exceto por criarem uma coluna do tipo de dados bigint. Deve ser usado bigserial se for antecipado o uso de mais de 231 identificadores durante o tempo de vida da tabela. Os nomes de tipo de dados smallserial e serial2 também funcionam da mesma maneira, exceto por criarem uma coluna do tipo de dados smallint.

A sequência criada para uma coluna serial é automaticamente excluída quando a coluna proprietária da sequência é excluída. É possível excluir a sequência sem excluir a coluna, mas isto força a remoção da expressão padrão da coluna.



[42] underflow: <calculadoras> estado onde a calculadora mostra um resultado zero para a parte mais significativa de um número, enquanto a parte menos significativa do número é eliminada. ISO/IEC 2382:2015(en) Information technology — Vocabulary