Atenção: por essa edição ser muito antiga não há arquivo PDF para download.
Os artigos dessa edição estão disponíveis somente através do formato HTML.

capnet43.jpg

Clique aqui para ler todos os artigos desta edição

 

PostgreSQL

Tipos de Dados e Seqüências

 

Este artigo irá explorar os tipos de http://kameha.devmedia.com.br/site/uploadsqlchttp://kameha.devmedia.com.br/site/netgold/edicao7sql/artigo4sql7.zip/SQLGold/artigo7.zipdados mais utilizados no PostgreSQL, bem como alguns exemplos do uso de seqüências. Esses dados serão divididos em quatro grupos: Caracter, Numéricos, Data e Hora e Booleano. Existem outros tipos não tão usuais dos quais estaremos tratando de forma introdutória.

 

Tipos de dados caracter

No PostgreSQL, existem três tipos básicos que podem comportar caracteres alfanuméricos: char, varchar e text. A diferença entre eles está nos limites e estrutura de armazenamento dos dados.

O tipo char(n) armazena uma cadeia de caracteres com tamanho fixo, tendo como n o seu tamanho máximo. Ocupa 4+n bytes em disco (se a cadeia de caracteres for menor que n, o campo é preenchido com espaços em branco à direita). Esses 4 bytes fixos são utilizados pelo PostgreSQL para armazenar informações referentes ao tipo e tamanho do campo.

O tipo varchar(n) tem como diferença possuir tamanho de armazenamento variável, limitado a n caracteres. Ocupa 4 + m bytes, onde m é o número de caracteres efetivamente usados.

O terceiro tipo, denominado text, possui tamanho variável e um limite muito grande de armazenamento. Equivale a um varchar(4000000000) e pode ser utilizado para armazenar campos BLOB do tipo texto.

 

Tipos de dados numéricos

Os tipos numéricos se subdividem em números inteiros, de precisão fixa, de ponto flutuante e os pseudo tipos seriais.

Inteiros

Esse tipo armazena números sem componentes fracionais. Números inteiros possuem limites máximos e mínimos, e qualquer tentativa de se armazenar valores fora dessa faixa irá gerar um erro. Existem os seguintes tipos inteiros:

 

smallint ou int2      2 bytes (16 bits)     

int, integer ou int4 4 bytes (32 bits) 

int8, bigint            8 bytes (64 bits)

 

Na maior parte dos casos, o tipo integer oferece a melhor relação performance versus capacidade de armazenamento. O tipo bigint, por ser mais pesado e conseqüentemente mais lento, deve ser usado somente quando a faixa de armazenamento do integer realmente não for suficiente.

 

Números de precisão fixa

 

Os números de precisão fixa são criados com o tipo numeric. Seu uso é recomendado para armazenar valores monetários e outras quantias nas quais a exatidão é necessária. No entanto, seu processamento e armazenamento são mais lentos quando comparados ao tipo float (que não possui precisão fracionária).

 

O tipo numeric pode receber dois parâmetros: precisão e escala. A precisão indica o número total de dígitos no número, e a escala determina quantos destes serão decimais. Por exemplo, o número 54,321 tem precisão 5 e escala 3. Inteiros podem ser considerados números que têm apenas escala 0. Para declarar um campo numeric, deve-se usar a sintaxe:

 

NUMERIC([precisao, [escala]])

 

Os parâmetros precisão e escala são opcionais. Caso a escala seja omitida, o valor assumido é zero, ou seja, a coluna não armazena números com precisão decimal e tem seu valor arredondado para o inteiro mais próximo. Uma coluna também pode ser declarada somente como numeric, sem a indicação de precisão e escala. Nesse caso, o dado armazenado pode conter um número indefinido de dígitos e precisão decimal infinita.

 

NOTA: O tipo decimal é sinônimo de numeric.

 

Números de ponto flutuante

 

Os tipos real (float4 ou float) e double precision (float8) armazenam valores numéricos inexatos, de precisão variável. São implementados de acordo com o padrão IEEE e são sempre armazenados como aproximações, podendo apresentar variações de arredondamento quando recuperados.

 

Nota

IEEE, lê-se “Eye-triple-E”, é acrônimo de Institute of Electrical and Electronics Engineers, órgão que define, entre outros, o padrão internacional para o formato de armazenamento digital de números científicos. Maiores informações no site http://standards.ieee.org

 

Na hora de decidir qual tipo de ponto flutuante deve ser usado, vale ressaltar:

 

·         Se houver necessidade de cálculos e armazenagem exatos (tal como em quantidade monetária), utilize o tipo numeric;

·         Em um float, o valor armazenado nem sempre é exatamente igual ao valor recuperado. Dessa forma, comparar dois valores de ponto flutuante em igualdade poderá ou não funcionar como o esperado. O ideal, nesse caso, é efetuar a comparação utilizando valores arredondados;

·         Quando a performance for crucial e diferenças de arredondamento puderem ser desprezadas, o ponto flutuante é mais indicado que o tipo de precisão fixa.

 

Arredondamento

 

No PostgreSQL existe a função ROUND (valor, escala), que permite arredondar um número até o limite da escala informada. Para escala igual a zero, o valor é arredondado para o inteiro mais próximo.

Existem diferenças nos padrões de arredondamento dos números de ponto flutuante e de precisão fixa. Enquanto o primeiro segue o padrão científico, o segundo segue o padrão monetário. Isso, às vezes, confunde o usuário, pois o valor retornado nem sempre é o que se espera. Por exemplo, veja na tabela 1 que o número 4.5 tem um comportamento diferente do esperado quando é arredondado. Isso se deve ao padrão de arredondamento, que no caso do tipo numeric é monetário: se um valor estiver exatamente entre dois inteiros, deve-se escolher o maior; e o tipo float possui arredondamento científico: se um valor estiver exatamente entre dois inteiros, será escolhido o valor par.

 

NOTA: Deve-se tomar cuidado com o padrão de arredondamento de várias linguagens de programação que seguem o modelo científico, como o Delphi. A melhor forma é “trazer” os valores devidamente arredondados na consulta. Dessa forma é garantido o mesmo resultado para todos os front-end utilizados.

 

Tabela 1

Numero Original

Round do valor numeric

Round do valor Float

3.4

3

3

3.5

4

4

4.4

4

4

4.5

5

4

5.4

5

5

5.5

6

6

 

O Tipo de Dados Serial

 

A maioria dos SGBDs possui alguma forma ou tipo de dados responsável pela autonumeração de colunas. No PostgreSQL este tipo é o serial. Seu funcionamento é explicado na seção Seriais e Seqüências.

Existe ainda uma variação do tipo serial - o serial8, que é equivalente ao int8 - ou seja, um numero inteiro de 64 bits. É normalmente utilizado em aplicações com grandes quantidades de INSERTs e DELETEs na mesma tabela, que possa quebrar o limite de 4 bilhões de registros do serial. 

Um exemplo de criação de uma tabela com uma chave primária utilizando o tipo serial é:

 

CREATE TABLE nomes

(

         id serial primary key,

         nome text

);

 

Deve-se notar que o tipo serial é sempre incrementado, e nunca volta a utilizar o mesmo número. É normal existirem "buracos" nos números, pois linhas podem ser apagadas, e inserções dentro de transações abortadas também incrementam o valor do campo serial.

Resumindo, o tipo serial é, na verdade, um tipo inteiro associado a uma seqüência.

Seqüências

Esse objeto produz uma seqüência numérica auto-incremento, que será sempre única, independente do contexto transacional. O valor gerado permanece associado ao nome do objeto seqüência. Podemos criar uma seqüência com o comando:

 

CREATE SEQUENCE nome_da_seqüência;

 

A manipulação de uma seqüência é feita através de três funções:

 

NEXTVAL('nome_da_seqüência') - Incrementa a seqüência em uma unidade e retorna o valor;

CURRVAL('nome_da_seqüência') - Recupera o último valor gerado por NEXTVAL na sessão; SETVAL ('nome_da_seqüência', valor) - Altera o valor da seqüência.

 

Nota-se que a função currval não retorna o valor atual da seqüência, mas sim o último valor obtido pela função nextval, na seção atual. A função currval trabalha de forma isolada entre as sessões de usuários do banco de dados. Por exemplo, imagine que a sessão X executou nextval, incrementando o valor da seqüência para 2; Neste momento a função currval, na seção X, retornará 2; Em seguida, a seção Y executa nextval, incrementando o valor da seqüência para 3; na seção X, currval continuará retornando 2, e na seção Y currval retornará 3; Se nextval for executado novamente na seção X, currval retornará 4 nesta seção; e assim por diante.

O valor atual da seqüência, independente do comando nextval, pode ser obtido com o comando SELECT * from nome_da_seqüência.

 

NOTA

A função currval só passa a retornar valor se a função nextval tiver sido utilizada pelo menos uma vez na seção.

 

Seriais e Seqüências

Quando é definido o tipo de uma coluna como serial, o PostgreSQL cria de forma automática um objeto seqüência e o associa a essa coluna. Por exemplo, o comando abaixo:

 

CREATE TABLE nomes

(

         id serial primary key,

         nome text

);

 

é executado internamente pelo PostgreSQL da seguinte forma:

 

CREATE SEQUENCE nomes_id_seq;

 

CREATE TABLE nomes

(

         id integer primary key default nextval('nomes_id_seq'),

         nome text

);

 

Ou seja, um campo serial é, de fato, um campo integer associado a uma seqüência. Assim, quando enviamos o comando INSERT INTO nomes(nome) values ('Roberto') e precisamos saber "qual ID foi atribuído ao Roberto", basta executarmos o comando SELECT currval('nomes_id_seq') logo em seguida.

 

NOTA: O nome da seqüência criada pelo tipo serial é composto por nome-da-tabela_nome-da-coluna_seq.

 

Veja mais um exemplo de uso da função currval na listagem 1. A chave primária da tabela pedidos foi definida como serial - dessa forma, a função currval é usada para preencher a chave estrangeira da tabela de itens, antes que a transação seja finalizada.

 

Listagem 1

 

BEGIN;

  INSERT INTO pedidos(date) VALUES (now());

 

  INSERT INTO itens(fk_pedido, nome, valor)

     VALUES (currval('pedidos_id_seq'), 'Caneta' ,  2);

  INSERT INTO itens(fk_pedido, nome, valor)

     VALUES (currval('pedidos_id_seq'), 'Lapis' ,  1);

  INSERT INTO itens(fk_pedido, nome, valor)

     VALUES (currval('pedidos_id_seq'), 'Borracha' ,  1.50);

  INSERT INTO itens(fk_pedido, nome, valor)

     VALUES (currval('pedidos_id_seq'), 'Calculadora' ,  45);

COMMIT;

 

Esses comandos podem ser enviados em um único round-trip, ou seja, em um único envio para o servidor PostgreSQL, para processá-los sem intervenção da aplicação cliente.

 

Nota

Se for passado um valor para os campos do tipo serial, a seqüência associada ao serial não é incrementada, o que pode criar problemas de chaves duplicadas. O ideal é nunca passar valor para campos seriais, a não ser que seja exatamente o que se queira fazer (por exemplo, numa situação de replicação de dados).

Tipos Data

Temos três tipos de dados para armazenamento de data: date, time e timestamp. O primeiro armazena somente a data, o segundo somente a hora e o terceiro a data e a hora em conjunto.

Para os tipos time e timestamp, pode ser utilizado o modificador with time zone, que acrescenta ao campo a informação do fuso horário.

Há três variáveis que podem ser usadas em conjunto com esses tipos de dados: CURRENT_DATE, CURRENT_TIME e CURRENT_TIMESTAMP. Elas retornam a data e hora atual e podem ser utilizadas em instruções SELECT, triggers e stored procedures, ou associadas a valores default de colunas.

 

Tipo de dados Booleano

O PostgreSQL fornece o tipo Boolean do SQL99. Esse tipo de dados só pode armazenar um dos dois estados: “true” ou “false”. Um terceiro estado, “unknown” (“desconhecido”), é representado pela declaração NULL do SQL. Observações:

 

?  Valores válidos para o estado “true” são: TRUE, ‘t’, ‘true’, ‘y’, ‘yes’, ‘1’.

?  Para o estado “false”, temos: FALSE,  ‘f’, ‘false’, ‘n’, ‘no’, ‘0’.

?  É preferível o uso das palavras-chave TRUE e FALSE.

 

Tipos de dados especiais

Além dos tipos básicos, o PostgreSQL ainda oferece diversas outras classes de tipos, que chamamos aqui de tipos de dados especiais. Esses tipos armazenam valores monetários, geométricos, valores referentes a endereços de rede, binários, valores do tipo BLOB, identificadores de objetos, matrizes, entre outros. Segue a definição de alguns desses tipos. Maiores detalhes podem ser obtidos na documentação do PostgreSQL.

 

Tipos especiais string e de data:

 

?  bit(n) - Equivale a um string com n caracteres formado por 0´s e 1´s com tamanho fixo. É preenchido automaticamente com zeros à direita caso o tamanho máximo do campo não seja utilizado;

?  varbit(n) - Possui a mesma características do bit, sendo que seu tamanho é variável. Está para o bit da mesma forma que o varchar está para o char;

?  Interval - Armazena um intervalo de tempo que pode ser expresso em segundos, minutos, horas, dias, semanas, meses, anos, décadas, séculos ou milênios.

 

Dentre os tipos geométricos, podemos citar:

 

·         Point - Armazena um ponto no espaço com coordenadas x e y. Sua representação é (x, y), ambos números de ponto flutuante;

·         Line - Um par de pontos que representam as coordenadas inicial e final de uma reta. Pode ser representado no formato: ((x1, y1), (x2, y2));

·         Box - Representa uma caixa retangular. É semelhante ao tipo line, sendo formado por dois pares de pontos;

·         Circle - Armazena a coordenada do centro e o raio de um círculo. Pode ser representado por ((x, y), r).

 

Dentre os tipos de dados de endereços de rede, podemos destacar o inet que representa um IP versão 4 válido. O formato de armazenamento é x.x.x.x/y, onde x.x.x.x é o endereço e y é o número de bits da máscara (netmask).

Conclusão

 

Nesse artigo vimos os tipos de dados essenciais do PostgreSQL. O uso correto desses tipos é fundamental para a boa performance e otimização do espaço ocupado pelo banco de dados.

 

Eduardo Hoelz Stern (ehstern@dbexperts.com.br), administrador de empresas formado pela Fundação Armando Álvares Penteado, em São Paulo, atua há 10 anos na área de informática, com especialidade em bancos de dados e desenvolvimento de sistemas.

Diretor de TI da dbExperts Brasil (www.dbexperts.com.br), desenvolveu a única versão do PostgreSQL com suporte nativo a Windows e Mac OS X, que originou a distribuição comercial dbExperts PostgreSQL.