Como utilizar diversos tipos de dados em uma única coluna no SQL

Propaganda
Como utilizar diversos tipos de dados em uma única
coluna no SQL Server
Pessoal, tudo bem?
Recentemente foi questionado por alguns alunos, em relação a capacidade do SQL
Server em reconhecer cada tipo de dados existente, como também, se existe a possibilidade
de armazenar em uma mesma coluna diferentes tipos de informações.
No momento desta pergunta, me veio a cabeça, um simples exemplo: “Utilize um
campo do tipo Varchar() ou Char(), desta forma, você poderá armazenar tanto valores
numéricos como também valores caracteres”. Até ai tudo bem. Mas a dúvida persistiu e
outro aluno me questionou.
Galvão, imagine a seguinte situação. “Uma coluna por exemplo denominada
Valores_Diversos, e nesta mesma coluna o SQL Server permitir que sejam armazenados,
dados do tipo, Integer, Varchar, Char, Float entre outros. Existe essa possibilidade?”. Pensei,
pensei, pensei e devolvi a resposta: “Olha acredito que exista pois o SQL Server possui um
mecanismo de armazenamento de dados em colunas muito versátil e volátil, mas eu não me
lembro o nome deste recurso ou tipo de dados. Vou procurar e volto com a resposta.”
Pois bem, seguindo alguns conselhos, informações, críticas e principalmente
querendo postar algum de conteúdo didático e que possa ajudar, comecei durante estes
dias estudar um pouco mais sobre as estruturas de armazenamento utilizadas pelo SQL
Server.
Dentre as estruturas, formas de armazenamento e tipos de dados, me deparei com o
chamado SQL_Variant. Pensei, SQL_Variant(Variante SQL), esta ai a resposta para estas
perguntas. Sendo assim me aprofundei no assunto, o que acabou resultando neste artigo.
Comecei então conhecer este tipo de dados e consegui identificar a possibilidade de
ser utilizado em diversos tipos de dados distintos, mas nem todos os tipos mais conhecidos
são possíveis de serem aplicados diretamente. Por outro lado, tipos de dados, como Char,
Int e Binary podem ser armazenados naturalmente.
Conhecendo o SQL_Variant:
O SQL_Variant foi introduzido no SQL Server a partir da versão 2000, com objetivo de
possibilitar a utilização em uma mesma coluna um conjunto distinto e variado de dados
relacionados a tipos e formatos diferentes.
A partir do momento em que utilizamos este tipo de dados, estamos forçando o SQL
Server a trabalhar de uma forma variável para realizar os armazenamento dos dados sobre
estas colunas, o que poderá resultar em pequenos aumentos no tempo de gravação dos
dados, além do que, podemos gerar um nível de fragmentação bem maior que se
estivessemos utilizando colunas separadas para armazenar cada dado.
Vale ressaltar que uma coluna do tipo SQL_Variant, pode armazenar no máximo
8.016 bytes de dados, isso incluindo:

Informações do tipo da base de dados que esta sendo utilizada;

Informações sobre o valor que esta sendo armazenado.
Com isso o tamanho máximo para armazenar dados para colunas SQL_Variant é de
no máximo 8.000 bytes.
Utilizar o SQL_Variant é bem simples, desde o processo de criação de uma table com
colunas SQL_Variant como também nos demais comandos utilizados pela Linguagem
DML(Data Manipulation Language – Linguagem de Manipulação de Dados).
O SQL_Variant trabalha de forma similar ao tipo de dados variant existente
Microsoft Visual Basic. Este tipo de dados, pode ser utilizado como:

Colunas;

Parâmetros; e

Variáveis.
Incompatibilidades apresentadas pelo SQL_Variant:
Como tudo no mundo, nada é perfeito, o SQL_Variant também apresenta as suas
limitações, dentre elas diversos tipos de dados que não podem ser utilizados no processo de
inserção de informações para colunas SQL_Variant. A tabela 1, a seguir apresenta a relação
dos tipos de dados incompatíveis com o SQL_Variant.
varchar(max)
varbinary(max)
nvarchar(max)
Xml
text
Ntext
image
Timestamp
sql_variant
Geography
hierarchyid
Geometry
Tabela 1 – Tipos de dados incompatíveis e não suportados pelo SQL_Variant.
Não somente tipos de dados o SQL_Variant não suporta, muitas das mais básicas e
comuns funções utilizadas pelo SQL Server, também não são suportadas por este tipo de
dados. A tabela 2, apresenta esta relação:
AVG
RADIANS
STDEV[P]
IDENTITY
ROUND
SUM
ISNUMERIC
SIGN
VAR[P]
POWER
Tabela 2 – Funções que não oferecem suporte ao SQL_Variant.
Por outro lado funções como , CAST e CONVERT oferecem suporte, além disso, uma
nova função de sistema conhecida como SQL_VARIANT_PROPERTY(), poderá ser utilizada
como forma de obtenção de informações relacionadas as propriedades e valores
armazenadas sobre uma coluna SQL_Variant.
Importante: As colunas SQL_Variant não possuem suporte a índices Full-Text, alémd
disso, não podem ser utilizadas em conjunto com as funções, CONTAINSTABLE e
FREETEXTTABLE.
Trabalhando com o SQL_Variant:
Agora que já conhecemos um pouco sobre este tipo de dados, suas características,
desvantagens e recursos nativos. Vamos começar a trabalhar diretamente com ele,
utilizando um cenário fictício, mas que poderá ser de grande aprendizagem. Para este
cenário vou criar uma tabela, utilizando algumas colunas dentre elas uma com o tipo
SQL_Variant.
Posteriormente faremos a inserção dos dados nos formatos Int, Char, Varchar e
Float. Em seguida, consultaremos os dados armazenados e por último será realizado uma
nova inserção de dados, que nos mostrará algumas surpresas existentes no SQL_Variant.
Montando o ambiente e fazendo uso do SQL_Variant:
Bom começamos a montar o nosso cenário, através da criação de uma base de dados
chamada Exemplos, em seguida criamos a tabela Produtos, nesta tabela faremos uso do
tipo de dados SQL_Variant aplicado a coluna Lote_Fabricação. Vale ressaltar que não é
necessário nenhuma configuração adicional para trabalhar com o SQL_Variant, muito
menos mudança no nível de compatibilidade do banco de dados. O Código 1 apresenta o
script de criação do banco de dados e da tabela Produtos.
Código 1 – Criando o Banco de Dados Exemplos e a Tabela Produtos:
CREATE DATABASE EXEMPLOS
GO
Use EXEMPLOS
Go
CREATE TABLE PRODUTOS
(CODIGO INT IDENTITY(1,1) Primary Key,
DESCRICAO VARCHAR(100),
LOTE_FABRICACAO SQL_VARIANT)
ON [PRIMARY]
Com o banco de dados e a tabela criada vamos inserir alguns registros em nossa
tabela. Observe que neste primeiro bloco de inserção de registros, estamos trabalhando
com dados no formatos: Varchar(), Char(), Int e Float.
Um detalhe importante que gostaria de destacar, esta relacionada a veracidade dos
dados, em nenhum registro foi respeito qualquer tipo de regra ou convenção que possa
existir quando se referimos a Lote de Fabricação de um Produto. O objetivo é expor e
apresentar a compatibilidade e armazenamento dos dados em diversos tipos e formatos. O
Código 2 apresentado a seguir ilustra o primeiro bloco de inserção de dados.
Código 2 – Inserindo o primeiro bloco de registros:
Insert Into PRODUTOS Values('Arroz','ABR-1009');
Insert Into PRODUTOS Values('Feijão','FEI-2010');
Go
Insert Into PRODUTOS Values('Bolacha 1',209310);
Insert Into PRODUTOS Values('Bolacha 2',93133);
Insert Into PRODUTOS Values('Bolacha 3',392873);
Insert Into PRODUTOS Values('Bolacha 5',209310);
Go
Insert Into PRODUTOS Values('Biscoito 1',5.25);
Insert Into PRODUTOS Values('Biscoito 2',2.00);
Insert Into PRODUTOS Values('Biscoito 3',3.250);
Insert Into PRODUTOS Values('Biscoito 4',1.553);
Go
Durante o processo os processos de manipulação de dados, oriundos dos comandos
Insert, Update e Delete. O SQL Server realiza uma análise de comparação para identificar o
tipo de dados e formato que esta sendo passado para inserção. Neste processo o SQL Server
mapeia os valores passados como parâmetro, com isso é possível estabelecer a
compatibilidade do valor ao tipo de dado ao qual ele pertence.
Todos os dados foram mapeados e inseridos de forma natural sem necessitar de
qualquer tipo de tratamento ou conversão implicita, para ilustrar a inserção dos dados, a
Figura 1 apresenta a Tabela Produtos e o primeiros registros inseridos.
Figura 1 – Dados inseridos na Tabela Produtos.
Com esta primeira massa de dados inseridas, vamos continuar nosso processo de
inserção de dados, agora trabalhando com dados no formato DateTime. Nesta parte,
poderá evidenciar as surpresas existentes pelo SQL_Variant, no processos de mapeamento
dos dados. Para isso, vamos utilizar o Código 3, responsável pela inserção de dados no
formato DateTime.
Código 3 – Inserindo o segundo bloco de registros, utilizando o formato DateTime
Insert Into PRODUTOS Values('Biscoito',2011-02-24);
Insert Into PRODUTOS Values('Biscoito',2011-02-25);
Insert Into PRODUTOS Values('Biscoito',2011-02-26);
Insert Into PRODUTOS Values('Biscoito',2011-02-27);
Insert Into PRODUTOS Values('Biscoito',2011-02-28);
Go
Nossos novos dados já foram inseridos, aparentemente sem nenhum problema, mas
se utilizarmos o comando Select para apresentar o atual conjunto de registros de nossa
tabela Produtos, teremos a surpresa. Os valores declarados para o campo Lote_Fabricacao
no comando Insert utilizado pelo Código 3, foram convertidas para o formato e tipo de
dados Integer, onde todos os valores de data foram recalculados e somandos para valores
que indicam um ano em nosso calendário. A Figura 2 apresenta o resultado do comando
Select.
Figura 2 – Dados inseridos no formato DateTime convertidos para o formato Integer.
Após observarmos os registros inseridos em nossa tabela e o campo Lote_Fabricacão
para as linhas convertidos em valores Integer. Nos apresenta alguns questionamentos:

Por qual motivo o SQL Server realizou a conversão dos Dados?

Existe alguma incompatibilidade em se utilizar tipos de dados DateTime?

Talvez seja necessário utilizar alguma função para mapeamento dos tipos de
dados e vincular aos valores especificados no comando Insert?
Para todas estas perguntas existem respostas que poderam esclarecer o que
aconteceu durante o processo de inserção. Trata-se de uma resposta simples e direta.
O SQL Server realizou a conversão dos valores para o tipo de dados Int, por entender
que estes valores podem ser armazenados neste formato, realizando um cálculado para
definir o valor que será armazenado, mesmo que o valor e seu formato esteja especificado
como DateTime. Para contornar esta característica do SQL_Variant, é necessário utilizar as
funções de conversão de dados implicitas Convert e Cast.
Não existe incompatibilidade do SQL_Variant para armazenar dados no formato
DateTime, existe sim uma atenção para se estabelecer o mapeamento do dado ao seu
formato, por isso devemos utilizar as funções de conversão implicatas.
Esta conversão pode parecer estranha ou sem sentido, mas na verdade não é basta
analisar a forma com que os valores estão sendo informados, o que força ainda mais o SQL
Server a entender que estamos trabalhando com uma fórmula matemática, como por
exemplo: 2011-02-24, se analisarmos podemos dizer que estamos fazendo a seguinte
equação: =2011-02-24 = 1985.
A tabela 3 apresentada a seguir demonstra o resultado de cada valor inserido como
DateTime mas convertido para Int.
Ano
2011
2011
2011
2011
2011
Mês
2
2
2
2
2
Dia
24
25
26
27
28
Equação Resultados
=2011-2-24
1985
=2011-2-25
1984
=2011-2-26
1983
=2011-2-27
1982
=2011-2-28
1981
Tabela 3 – Resultados da conversão de DateTime para Integer.
Neste momento o que nos resta a fazer é realizar novamente a inserção dos dados
no formato DateTime, mas fazendo uso da função Convert para que o mapeamento seja
realizado de forma compatível com o formato desejado. O Código 4 apresentado abaixo
demonstra como podemos fazer esta nova inserção.
Código 4 – Inserindo o segundo bloco de registros, utilizando o formato DateTime e
função Convert.
Insert Into PRODUTOS Values('Biscoito',Convert(DateTime,2011-02-24));
Insert Into PRODUTOS Values('Biscoito', Convert(DateTime,2011-02-25));
Insert Into PRODUTOS Values('Biscoito', Convert(DateTime,2011-02-26));
Insert Into PRODUTOS Values('Biscoito', Convert(DateTime,2011-02-27));
Insert Into PRODUTOS Values('Biscoito', Convert(DateTime,2011-02-28));
Go
Após esta nova inserção de dados utilizando a função de conversão Convert nossos
dados estão armazenados de forma correta respeitos seu formato e tipo de dados.
Conclusão:
Utilizar uma coluna do tipo SQL_Variant nos apresenta diversas vantagens quando
temos a necessidade de armazenar informações em uma tabela sem se preocupar com o
tipo de dados, formato ou se este dado é compatível com a coluna a qual esta sendo
inserido. Por outro lado, nem sempre temos esta possibilidade e como podemos observar
alguns tipos de dados, dentre eles, o Datetime necessitou de um tratamento antes de ser
armazenado, fazendo-se uso de funções de conversão implicitas de dados Convert e Cast.
Espero que este artigo posso ter respondido as dúvidas dos meus alunos e
apresentado para você leitor como podemos trabalhar com dados distintos na mesma área
de armazenado.
Agradeço a sua atenção, nos vemos no próximo artigo, até mais.
Download