Esta página foi traduzida por máquina. Leia o original em inglês. English

Biblioteca IBSurgeon

Tipos de dados

NOTICE: Este documento é o capítulo do livro “The InterBase World”, escrito por Alexey Kovyazin e Serg Vostrikov.

Apesar de os tipos de dados serem descritos em detalhes na documentação (ver [1, capítulo 4.), é necessário considerar uma série de noções que serão usadas com frequência nos capítulos seguintes deste livro. Além de informações gerais, também serão revisados vários exemplos de uso de tipos de dados em bancos de dados InterBase e recomendações sobre seu uso e conversão. Consideraremos em detalhes as distinções de tipos de dados existentes no 1º e no 3º dialeto de banco de dados InterBase.

Sobre dados

Tipos de dados são elementos básicos de qualquer linguagem de programação ou de qualquer servidor de SGBD. O InterBase não é exceção. Quando dizemos que um banco de dados armazena algumas informações, devemos perceber que essas informações não podem ser armazenadas em um único lugar. Pelo contrário; os dados devem ser classificados e colocados em sua “prateleira”. Os tipos de dados definem o que pode ser colocado em uma determinada “prateleira” e o que não pode. Quando falamos sobre a “prateleira”, primeiro nos referimos aos campos das tabelas do banco de dados (ver capítulo “Tabelas. Chaves primárias e geradores” (parte 1)), bem como às variáveis dentro de triggers e procedimentos armazenados, etc. Cada tipo de dado tem um conjunto de operações que podem ser executadas sobre os valores desse tipo. Portanto, é importante escolher o tipo de dado correto ao projetar um banco de dados. Isso ajudará a evitar muitos problemas ao desenvolver programas clientes. No InterBase, existem 12 tipos de dados que podem atender aos requisitos do desenvolvedor no armazenamento de dados. Esses tipos são condicionalmente subdivididos nos 6 grupos seguintes:

  • para armazenar inteiros - INTEGER e SMALLINT;
  • para armazenar números reais - FLOAT e DOUBLE PRECISION;
  • para armazenar números com precisão fixa - NUMERIC e DECIMAL;
  • para armazenar data, hora e timestamp - DATE, TIME e TIMESTAMP;
  • para armazenar símbolos - CHARACTER (na forma abreviada - CHAR) e VARYING CHARACTER (VARCHAR);
  • para armazenar dados dinamicamente extensíveis - BLOB

Além disso, é possível definir arrays de valores de tipos primitivos, ou seja, todos os tipos enumerados, exceto BLOB.

A maioria dos tipos de dados do InterBase corresponde aos tipos definidos no padrão SQL92, no entanto, além disso, há peculiaridades próprias - arrays de tipos de dados primitivos e BLOB. Arrays no InterBase podem conter um conjunto de dados do mesmo tipo em um único campo. Por exemplo, podemos definir um array de valores do tipo INTEGER. Arrays podem ter várias dimensões. O tipo de dado BLOB é um tipo de dado dinamicamente extensível, cujo nome é frequentemente decifrado como Binary Large OBject. Deve-se dizer que o BLOB é uma invenção dos desenvolvedores do InterBase que mais tarde se espalhou e se estabeleceu em todos os servidores SQL contemporâneos.

Sintaxe de definição de tipos de dados

Tipos de dados são usados para descrever campos em tabelas, variáveis em triggers e procedimentos armazenados. A sintaxe comum para definir todos os tipos de dados possíveis no InterBase é fornecida abaixo.

< datatype> =

{SMALLINT | INTEGER | FLOAT | DOUBLE PRECISION}[ ]

| {DATE | TIME | TIMESTAMP} [ ]

| {DECIMAL | NUMERIC} [( precision [, scale])] [ ]

| {CHAR | CHARACTER | CHARACTER VARYING | VARCHAR} [( int)

[ ] [CHARACTER SET charname]

| {NCHAR | NATIONAL CHARACTER | NATIONAL CHAR}

[VARYING] [( int)] [ ]

| BLOB [SUB_TYPE { int | subtype_name}] [SEGMENT SIZE int]

[CHARACTER SET charname]

| BLOB [(seglen [, subtype])]

As características dos tipos de dados, como tamanho, precisão e faixa de valores possíveis, são descritas em detalhes na tabela 4.1 em [1., portanto não vamos nos repetir aqui. Agora, vamos revisar brevemente as principais peculiaridades dos tipos de dados e focar em sua possível aplicação.

Tipos inteiros

SMALLINT e INTEGER referem-se a tipos inteiros. Devo dizer que SMALLINT representa uma versão limitada do INTEGER. Seu comprimento é de 2 bytes, ao contrário dos 4 destinados ao armazenamento de INTEGER. Normalmente, você não deve economizar espaço em disco. Com base nisso, é melhor usar o tipo INTEGER para armazenar valores inteiros.

A área de aplicação dos tipos inteiros é óbvia: eles são necessários para campos que contêm apenas inteiros - para armazenar contadores, números, etc. Normalmente, o tipo INTEGER também é usado para campos que contêm chaves primárias.

Tipos de dados reais

Os tipos FLOAT e DOUBLE PRECISION referem-se aos reais (também chamados de tipos numéricos com ponto flutuante). Primeiro, quero alertar o leitor contra o uso do tipo FLOAT - sua precisão não é suficiente para armazenar a maioria dos valores fracionários. Especialmente, não é recomendado armazenar valores monetários nele - em variáveis do tipo FLOAT, erros de arredondamento aparecem muito rapidamente e isso pode surpreender um contador ao fazer cálculos. A melhor maneira de armazenar números com ponto flutuante (por exemplo, em sistemas contábeis e sistemas para cálculos científicos) em um banco de dados é armazená-los no tipo DOUBLE PRECISION.

Você deve levar em conta que, no 3º dialeto do InterBase, existe um mecanismo de armazenamento de tipos com ponto fixo de 64 bytes. Esse mecanismo é usado para armazenar valores monetários. O uso desses tipos fornece a melhor precisão.

Tipos de dados com ponto fixo

NUMERIC e DECIMAL referem-se a esses tipos de dados. A pergunta sobre a diferença entre NUMERIC e DECIMAL é muito frequente. Ambos os tipos têm uma capacidade de dígitos idêntica - de 1 a 18 sinais, precisão idêntica - de zero até a capacidade de dígitos.

Lembremos que: a capacidade de dígitos é o número total de dígitos, e a precisão - o número de sinais após a vírgula.

O mais engraçado é que esses tipos diferem na capacidade máxima de dígitos de acordo com a documentação, mas, na prática, são realizados quase igualmente e não há diferença entre eles.

Você pode verificar facilmente isso, iniciando o utilitário isql e executando a sequência de ações abaixo. Criamos a tabela da seguinte forma:

SQL> CREATE TABLE test (

CON> Num_field NUMERIC(15,2),

CON> Dec_field DECIMAL(15,2));

Em seguida, damos um comando para mostrar a estrutura da tabela:

SQL> show tables test;

E vemos o seguinte:

NUM_FIELD NUMERIC(15, 2) Nullable

DEC_FIELD NUMERIC(15, 2) Nullable

Como você pode ver, o InterBase informa que ambas as colunas fornecidas têm o tipo NUMERIC. As razões para esse comportamento estão na realização de tipos de dados com ponto fixo. O problema é que o InterBase tem apenas três mecanismos para armazenar qualquer expressão inteira, e todos os tipos, independentemente de como são nomeados, são realizados de acordo com essas variantes.

Aqui está a tabela de [1., que ilustra como vários tipos inteiros (tabela 1.1) são armazenados. Como você pode ver, o armazenamento de dados no 3º dialeto é diferente para números com grande capacidade de dígitos:

Tabela 1.1. Armazenamento de números com ponto fixo

Capacidade de dígitos Dialeto 1 Dialeto 3
de 1 a 4 SMALLINT para NUMERIC
INTEGER para DECIMAL
SMALLINT
de 5 a 9 INTEGER INTEGER
de 10 a 18 DOUBLE PRECISION INT64

Agora podemos afirmar com certeza quais são as principais diferenças entre os tipos NUMERIC e DECIMAL: no caso de definir um campo (ou variável) com pequena capacidade de dígitos (até quatro), o primeiro é armazenado como inteiro de 2 bytes SMALLINT, e o segundo - como INTEGER de 4 bytes. Assim, se a capacidade de dígitos for maior que quatro, os tipos DECIMAL e NUMERIC serão equivalentes.

Preste atenção à diferença de realização de tipos com grande capacidade de dígitos no primeiro e no terceiro dialetos. No primeiro dialeto, o número com ponto fixo passou de inteiro para real, onde os mecanismos de arredondamento eram aplicados. No terceiro dialeto, essa peculiaridade foi eliminada - os grandes inteiros são armazenados realmente como inteiros - usando o mecanismo INT64, que pode armazenar números de 64 bits em uma faixa de +/-2^32. Portanto, seria melhor armazenar dados sobre fundos monetários em bancos de dados criados com o uso do 3º dialeto. Somente usando o mecanismo INT64 será garantida a segurança dos pequenos restos monetários.

Tipos para armazenar data e hora

Os tipos para armazenar data e hora mudaram na versão InterBase 6.x e seus clones em comparação com 4.x e 5.x. Para não nos confundirmos em teias históricas com esses tipos, consideraremos a situação na versão 6 do InterBase. Depois, com base nisso, mencionaremos brevemente o que era antes. Isso é feito para aqueles usuários que ainda trabalham com versões anteriores do InterBase. Então, existem 3 tipos no InterBase 6.x para armazenar data e hora - DATE, TIME e TIMESTAMP.

  • O tipo DATE armazena datas com precisão de um dia. Uma faixa de valores possíveis - de 1º de janeiro de 100 d.C. até 29 de fevereiro de 32768.
  • O tipo TIME armazena dados sobre hora com precisão de um décimo de milésimo de segundo. Uma faixa de valores possíveis - de 00:00 até 23:59.9999.
  • O tipo TIMESTAMP representa uma combinação dos tipos DATE e TIME.

Como trabalhar com datas? Se a questão é sobre o trabalho no nível do servidor em procedimentos armazenados ou triggers, tudo é bastante simples - podemos sempre declarar uma variável do tipo necessário e defini-la a partir das tabelas e vice-versa. No entanto, é necessário transferir os dados de um banco de dados para o aplicativo e vice-versa. Nesse caso, há duas abordagens: usar bibliotecas que aplicam um formato original de datas do InterBase para acessar objetos desses tipos e converter esse formato para tipos de data/hora intralinguagem comuns (FIBPlus é um exemplo de tal biblioteca), ou usar o mecanismo de transformação de datas em strings, embutido no InterBase.

O que você fará se precisar extrair apenas o ano ou o mês da data completa? Você terá que usar um grupo de funções EXTRACT para esse propósito (disponíveis em todos os clones do InterBase 6.x), que permitem extrair apenas a parte necessária da data. Essas funções são usadas da seguinte maneira:

EXTRACT (MONTH FROM DATE_FIELD)

EXTRACT (YEAR FROM DATE_FIELD)

A lista completa de parâmetros na função EXTRACT é a seguinte: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEKDAY, YEARDAY. Sua função segue de seu nome, portanto não vamos interpretá-las aqui.

Tipos de dados para armazenar texto

Existem dois tipos no InterBase destinados a armazenar informações de texto - CHAR e VARCHAR. Seus nomes completos - CHARACTER e CHARACTER VARYING, no entanto, não há razão para usar nomes longos - até mesmo o comando Show tables no utilitário isql fornece nomes abreviados dos tipos.

Para definir um campo ou uma variável de tipo simbólico, é necessário especificar entre parênteses, após o nome do tipo, um número de símbolos que serão usados em um objeto definido, ou omitir o número de símbolos - assim, o campo com comprimento de 1 símbolo será criado.

CREATE TABLE testCHARLen(

Field1 CHAR(255),

Field2 CHAR);

Quando esta tabela for criada, Field1 terá comprimento de 255 símbolos, e Field2 - 1 símbolo.

Os tipos CHAR e VARCHAR são semelhantes em muitos aspectos - ambos podem conter até 32768 símbolos, no entanto, há algumas diferenças. Embora ambos os tipos sejam armazenados em um banco de dados da mesma maneira, o InterBase trabalha com eles de formas diferentes. O exemplo a seguir demonstra isso:

SQL> create table testCHAR ( c1 char(10), c2 varchar(10));

SQL> insert into testCHAR(c1,c2) values(‘Test’,‘Test’);

SQL> SELECT ‘(’||c1.|’)’, ‘(’||c2.|’)’ from testCHAR;

No resultado, obteremos o seguinte:

(Test ) (Test)

Como você pode ver, após o valor ‘Test’, selecionado de um campo c1, havia espaços em branco. Isso significa que, ao selecionar dados de um campo do tipo CHAR, o valor retornado é complementado com espaços em branco até o comprimento total do campo. É difícil supor por que esse comportamento, que leva a um crescimento substancial do tráfego de rede (sobrecarga da rede), é necessário. Em qualquer caso, VARCHAR é um tipo simbólico recomendado para uso.

Uma das principais características do tipo simbólico é seu conjunto de caracteres - CHARACTER SET. O conjunto de caracteres é definido para todo o banco de dados e usado por padrão para todos os campos simbólicos, se não for redefinido explicitamente ao criar um campo. Para criar um campo simbólico com a indicação explícita de um conjunto de caracteres, é necessário adicionar a descrição de um conjunto de caracteres na descrição da coluna (nas sentenças CREATE TABLE ou ALTER TABLE). O conjunto de caracteres WIN1251 é geralmente usado para o suporte ao russo (se você quiser saber em detalhes sobre o uso do russo no InterBase, veja o capítulo “InterBase Russification” (parte 1)). Aqui está um exemplo de uma tabela contendo um campo simbólico com o conjunto de caracteres WIN1251 explicitamente descrito:

CREATE TABLE TestCHARSET(

Field1 VARCHAR(255),

Field2 VARCHAR(255) CHARACTER SET win1251);

Aqui, Field1 é o campo sem a indicação explícita de um conjunto de caracteres, portanto, o conjunto de caracteres especificado ao criar o banco de dados será usado para ele. É obviamente determinado para o campo Field2 que ele armazenará símbolos na codificação WIN1251.

Além de indicar um conjunto de caracteres para campos simbólicos, você também pode especificar uma ordem de classificação (collation) que define como os símbolos deste conjunto de dados serão ordenados. Para o russo, existem duas variantes de collation - WIN1251 e PXW_CYRL. Para mais detalhes sobre o uso de COLLATION ORDER, consulte o capítulo “Russificação do InterBase”.

Você pode encontrar uma lista completa de conjuntos de caracteres e COLLATION ORDER aplicados a eles na documentação [1, capítulo 13..

Atenção! De acordo com a documentação do InterBase 6, existem 4 tipos simbólicos: além dos tipos de dados mencionados acima, existem mais 2 - NCHAR e NCHAR VARYING, porém a mesma documentação abaixo explica que os dois últimos tipos são os mesmos que CHAR e VARCHAR, apenas com o conjunto de caracteres ISO8859.1 usado por padrão. Isso significa que, na prática, usar o pseudo-tipo NCHAR é equivalente a aplicar CHAR DEFAULT CHARACTER SET ISO8859.1. Da mesma forma para NCHAR VARYING, só que em vez de CHAR, usa-se VARCHAR. É óbvio que a aplicação desses pseudo-tipos é destinada a usuários da Europa Ocidental e dos EUA, onde um conjunto de caracteres ISO8859.1 é criado para o suporte de idiomas.

Tipo de dados BLOB

O tipo de dados BLOB é destinado a armazenar uma grande quantidade de dados de tamanho variável. O tipo BLOB permite armazenar dados que não podem ser colocados em campos de outros tipos - por exemplo, imagens, arquivos musicais, fragmentos de vídeo, etc. Os requisitos para definir o campo mais simples do tipo BLOB na tabela são os mesmos que para definir um campo de qualquer tipo elementar:

CREATE TABLE testBLOB(

myBlobField BLOB);

Como resultado, será criado o campo myBlobField no qual é possível armazenar dados consideráveis. Apesar do fato de que os campos BLOB não diferem dos outros na forma de definição, sua realização dentro de um banco de dados difere muito. Campos não-BLOB estão localizados na página de dados (veja o capítulo “Estrutura do banco de dados InterBase” (parte 4)) próximos uns dos outros, e enquanto apenas o identificador BLOB é armazenado na página de dados, o próprio BLOB é alocado em uma página especial. Tal estrutura de dados permite armazenar dados de tamanho não fixo.

O tipo BLOB tem a possibilidade de definir um conjunto de vários subtipos e procedimentos especiais chamados filtros (BLOB filters) para trabalhar com esses subtipos. Existem alguns subtipos BLOB pré-determinados embutidos no InterBase. Todos esses subtipos têm números não negativos, por exemplo, subtipo 0 - dados de tipo indefinido, subtipo 1 - texto, subtipo 2 - BLR (Binary Language Representation, veja o glossário e o capítulo “Estrutura do banco de dados InterBase”), etc. Um usuário também pode definir subtipos BLOB que podem ter valores negativos. O filtro pode ser aplicado a cada tipo. Ele converterá um campo de um subtipo em outro.

Deve-se notar que o uso de campos BLOB é geralmente uma alternativa ao armazenamento de arquivos externos em relação ao banco de dados. Quanto aos filtros BLOB, eles são usados muito raramente devido à orientação a uma categoria estreita de tarefas.

Arrays

O SGBD InterBase foi um dos primeiros em que arrays apareceram. O suporte a arrays em um banco de dados é a extensão do modelo relacional tradicional. A presença de arrays permite simplificar o trabalho com conjuntos de um mesmo tipo de dados.

O array é uma coleção de valores de um tipo, tendo um nome comum e permitindo endereçar qualquer elemento do array de acordo com seu número. Arrays no InterBase podem ser unidimensionais e multidimensionais.

Para criar um campo de array de números INTEGER na tabela, deve-se escrever algo como o seguinte:

CREATE TABLE test(

myOneDimArray INTEGER[12.,

myTwoDimArray INTEGER[5,4.,

myThreeDimArray INTEGER[2,10,8.);

Assim, três campos do tipo array serão criados: myOneDimArray - um campo contendo um array unidimensional de 12 números de comprimento, myTwoDimArray, contendo um array bidimensional (matriz) - 5х4 Integers, e myThreeDimArray - um campo contendo um array tridimensional 2х10х8. Deve-se destacar que, sob tal definição, os elementos do array são numerados começando de “um”, ou seja, o primeiro elemento tem número 1, o segundo - número 2, etc. Se alguém quiser definir os limites do array por conta própria, por exemplo de 0 até 5, deve especificar a definição do campo da seguinte forma:

myArray INTEGER[0:5.

Arrays são realizados com base em campos do tipo BLOB, portanto, você não deve temer que o array multidimensional “polua” sua tabela com uma grande quantidade de dados: o InterBase alocará cuidadosamente os dados do array em páginas separadas para otimizar as operações de entrada e saída nesses campos. Como usar arrays? Eles fornecem um mecanismo conveniente para armazenar objetos do mesmo tipo. Porém, em 80% dos casos, em vez de arrays, os desenvolvedores preferem armazenar dados múltiplos em tabelas subordinadas (detalhe), portanto, arrays não são tão frequentemente usados em aplicações cliente do SGBD InterBase. Isso acontece porque as bibliotecas de acesso fornecidas com Delphi e C++ Builder, como BDE e IBX, são incapazes de trabalhar com arrays. De acordo com a documentação do InterBase, é possível trabalhar com arrays usando o pré-processador gpre, porém esta não é a maneira mais conveniente para o desenvolvedor Delphi/C++ Builder. Felizmente, na biblioteca FIBPlus há suporte para campos-array no InterBase. Você pode ler sobre isso em detalhes no capítulo “Habilidades especiais do FIBPlus”. A biblioteca cliente IBProvider, que permite criar aplicações cliente para InterBase usando ferramentas de desenvolvimento da Microsoft, também suporta o trabalho com arrays (veja o capítulo “Desenvolvimento de aplicações cliente do SGBD InterBase usando a tecnologia Microsoft OLE DB” (parte 3)).

Conclusão

Deve-se destacar que é impossível falar sobre tipos de dados sem avançar, porque eles penetram em todas as áreas-chave relacionadas ao desenvolvimento de aplicações de banco de dados. Portanto, ao ler este livro, é melhor usar o capítulo dado como referência rápida à qual você pode recorrer sempre que precisar refrescar os fundamentos do InterBase.