Exemplo de otimização de armazenamento de dados
favorito (4) anotar marcar como lido dúvidas?
Caio Azevedo (e-mail) é arquiteto de softwares, especialista em soluções corporativas nos ambientes intranet/internet. Graduado em Ciência da Computação, Mestrando da Escola Politécnica da USP. Certificações Microsoft [MCP, MCAD e MCSD .NET]. Gestor da célula Microsoft da Magna Sistemas Consultoria (www.magnasistemas.com.br).
Olá Pessoal! Esse artigo tem como objetivo propor a solução de um problema no qual deparamos com razoável freqüência, e se você ainda não o teve pela frente, provavelmente ainda o terá especialmente se você desenvolve aplicações comerciais.
Meu grande objetivo com esse artigo, bem mais que propor uma solução, é despertar nos leitores o prazer obtido durante o processo a racionalização e solução otimizada de mais um, dos vários problemas que deparamos em nosso dia a dia.
Imaginemos o cenário onde precisamos gerenciar eventos, por exemplo, no desenvolvimento de uma agenda. Uma das características de um evento é sua periodicidade, que pode ser, única, diária, semanal e mensal. Suponhamos que na periodicidade semanal, além de informar a quantidade de semanas de ocorrência do evento, seja necessário especificar os dias da semana que o evento ocorrerá, por exemplo:
. As aulas do curso de C# ocorrerão a partir do dia 30/10/2006, nas próximas 4 semanas às segundas, quartas e sextas-feiras.
. As reuniões de acompanhamento do projeto, ocorrerão a partir do dia 01/11/2006, pelos próximos 4 meses.
. A apresentação do projeto será realizada dia 20/12/2007.
Proposta de modelo de dados
Uma modelagem de dados muito utilizada seria algo semelhante ao apresentado abaixo, ou algo muito próximo disso, com uma tabela somente com os dias da semana relacionada à tabela de eventos.
EVENTO_DIA_QUAINTA Bit
Tabela 1- modelo parcial da tabela de eventos
Onde, o atributo TIPO_FREQUENCIA é definido em um domínio pré-estabelecido, por exemplo [1-único, 2 – diário, 3 – semanal, 4 - mensal].
Os atributos TOTAL_DIAS, TOTAL_SEMANAS, TOTAL_MESES, contabilizam o período de ocorrência do evento para cada tipo da freqüência.
E finalmente os atributos DIA_XXX, especificam os dias da semana de ocorrência do evento.
O processo de manutenção dessas informações será deveras penoso sob os seguintes aspectos:
. Temos uma quantidade razoável de atributos, onde sempre haverá desperdício de espaço em nosso repositório de dados.
. A codificação terá que contemplar todos esses atributos [procedures com muitos parâmetros, classes com todas essas propriedades, métodos de validação e operações de CRUD com vários parâmetros].
. Processo de alteração mais complexo – um evento inicialmente cadastrado como semanal, 4 vezes, às tercas, quintas e sábados ao ser modificado para mensal, 2 vezes, serão necessários instruções adiocionais para atualizar todos os atributos.
Nossa solução propõe uma redução drástica na quantidade de atributos na tabela de eventos.
EVENTO_QUANTIDADE_FREQUENCIA Int
Tabela 2- modelo parcial da tabela de eventos - solução proposta
Aqui, o atributo QUANTIDADE_FREQUENCIA armazena o total de ocorrência do evento, seu sentido será dado em função do TIPO_FREQUENCIA.
E, finalmente, a grande otimização do cenário, em um único atributo - DIAS_DA_SEMANA , do tipo byte, armazenaremos os dias da semana e todas as suas combinações, caso, claro, tenhamos uma freqüência do tipo semanal. Obviamente ainda poderemos ter "desperdício" de um atributo, mas somente para o tipo da freqüência diferente de semanal.
Mas como armazenar tantas informações em um único atributo, e do tipo byte?
Recordar é viver – os tipos de dados fundamentais são formados por conjuntos de bits – assim, um inteiro possui 4 bytes x 8 bits = 32 bits, um double possui 8 bytes x 8 bits = 64 bits, etc. No nosso caso, onde temos que manipular 7 informações (os dias da semana), a melhor opção é um campo do tipo byte que tem 8 bits, tudo bem, ainda desperdiçamos 1, mas nem tudo é perfeito.
0000 0001 01
0000 0010 02
0000 0100 04
0000 1000 08
Tabela 3- um byte em bit
0000 0001 01 domingo
0000 0010 02 segunda-feira
0000 0100 04 terça-feira
0000 1000 08 quarta-feira
0001 0000 16 quinta-feira
0010 0000 32 sexta-feira
Tabela 4 - dias e valores correspondentes
Matematizando os dias da semana:
0000 0001 01 ? 2 0 domingo
0000 0010 02 ? 2 1 segunda-feira
0000 0100 04 ? 2 2 terça-feira
0000 1000 08 ? 2 3 quarta-feira
0001 0000 16 ? 2 4 quinta-feira
0010 0000 32 ? 2 5 sexta-feira
0100 0000 64 ? 2 6 sábado
Tabela 5 - dias e valores correspondentes
Ora, concluímos que o valor associado à um dia da semana corresponde ao número 2 elevado ao seu "peso", 2 (dia da semana). E para combinação de vários dias como nos exemplos acima? Simples, é só "ligar" os bits correspondentes à cada dia, vejamos: + Segunda, Quarta e Sexta == 0010 1010 == 2 1 + 2 3 + 2 5 == 02 + 08 + 32 == 42 + Terça, Quinta e Sábado == 0101 0100 == 22 + 24 + 26 == 04 + 16 + 64 == 84.
Logo, ao invés de manipular diversos atributos, propriedades e parâmetros na codificação, calculamos um único valor numérico que representa a combinação de todos os dias da semana e assim manipulamos somente um atributo, simplificando e otimizando nosso trabalho.
Finalmente, a expressão para obtenção desse valor será : Valor += 2 (dia da semana) , onde o dia da semana será obtido de sua interface.
E o processo inverso?
Como recuperar o valor armazenado no repositório de dados e disponibilizá-lo ao usuário? Ora, precisamos desse valor representado em bits novamente, ou melhor, em um conjunto de bits (7), em seu devido estado fundamental – ligado x desligado, mais precisamente devido interação com a interface true x false. Mais uma vez recorremos à maravilhosa matemática. Nosso algoritmo foi baseado em funções exponenciais, onde uma das formas de análise dessas funções é o processo conhecido como fatoração, lembram?
Tabela 6 - fatoração de 64 na base 2, 64 = 26
Pois bem, precisamos de um conjunto, ou array de bits ligados e desligados, correspondente ao valor armazenado, combinando as operações de fatoração e resto das divisões sucessivas teremos exatamente as informações que precisamos:
O resto da divisão 42 / 2 = 0 (fatorando = 21)
O resto da divisão 21 / 2 = 1 (fatorando = 10)
O resto da divisão 10 / 2 = 0 (fatorando = 05)
O resto da divisão 05 / 2 = 1 (fatorando = 02)
O resto da divisão 02 / 2 = 0 (fatorando = 01)
Tabela 7 - Combinando, fatoração e resto das divisões
Esses "zeros" e "uns" gerados, tomados de baixo para cima , corresponde exatamente ao nosso conjunto inicial de bits (10 1010), complementando-os com zeros à esquerda, bingo: 0010 1010, ou melhor, um array do tipo [false – false – true – false true – false – true –false].
Codificação: Para melhor compreensão de nossa proposta, codificaremos tudo que foi dito até agora.
Interface Sugerida
Valor correspondente aos dias da semana
Array de booleanos correspondente ao valor armazenado
Finalmente atribuimos ao checkBoxList o resultado do seu array de booleanos
É isso ai pessoal, espero ter atingido meu objetivo, despertando nos caros colegas a sensação de que nosso trabalho deve ir muito além de pesquisar no google códigos para resolução de problemas.
Precisamos sim, analisar as situações, racionalizar os problemas (sem esquecer a intuição) e propor soluções da melhor forma possível. Como vimos, especialmente depois de pronto, a codificação utilizada foi mínima, e é ai que está o grande mérito da proposta, conseguimos muito com bem pouco, em mais uma demonstração da lei natural do mínimo esforço (não à toa, a matemática foi nossa principal ferramenta).
Se vocês concluíram que pequenas idéias (geralmente fruto da imaginação) podem fazer grande diferença em nossas carreiras, já estarei satisfeito, afinal como dizia o grande Albert Einstein, "A imaginação é mais importante que o conhecimento".
E, finalmente, segue um pequeno projeto utilizado em nosso artigo. Sintam-se à vontade para qualquer esclarecimento/crítica/sugestão.