Seções iMasters
Banco de Dados + SQL Server

Tabelas e mais tabelas

Um assunto muito importante para quem trabalha com bancos de dados com problemas de desempenho e fora do controle é o uso excessivo de tabelas. Veremos quais são as principais causas deste problema, os efeitos negativos gerados por esta prática e algumas soluções para evitar o entupimento do banco de dados com tabelas.

Antes de começar a falar sobre o problema, é bom deixar claro que estou falando sobre tabelas adicionais, criadas depois que o modelo de banco de dados foi estabelecido. Nas situações normais, cada tabela deve ser cuidadosamente planejada de acordo com o modelo de dados, pois assim temos uma consistência e uma correlação entre a realidade e o objeto do banco de dados.

Posto isso, vou falar sobre aquelas tabelas que geralmente são criadas no banco de dados para auxiliar a elaboração de consulta ad hoc, importações/exportações não suportadas pelo sistema ou simplesmente para resolver um problema urgente. O grande mal aqui não é tanto a criação destas tabelas, mas sim a falta de manutenção e remoção das mesmas. Sim, é a famosa gambiarra (também conhecida como SABC – Solução Alternativa de Baixo Custo) que resolve o problema rapidamente, porém acaba ficando para trás no banco de dados.

Mais uma vez, infelizmente eu encontro muitos cenários com tabelas auxiliares, temporárias e que não foram modeladas para suportar o sistema quando forneço serviços de consultorias em banco de dados. Assim como os problemas relacionados à tipagem errada e desfragmentação, o uso indiscriminado de tabelas gera vários problemas e é uma péssima prática operacional.

Existem várias causas para a criação de tabelas fora do modelo de dados dos sistemas. No contexto do SQL Server, a maioria das tabelas fora do modelo de dados é criada pela cláusula INTO da instrução SELECT. Geralmente funciona assim: o desenvolvedor está trabalhando em uma rotina e monta uma instrução SELECT. Porém ele nota que vai precisar dos dados desta instrução para algum processamento ou outro fim. Portanto, ele adiciona a cláusula INTO À instrução SELECT e dá um nome de uma tabela para armazenar o conjunto de resultados. Geralmente isso ocorre para copiar dados de forma temporária e que serão utilizados apenas uma vez em uma situação específica.

Juntar o acesso direto ao banco de dados através de uma ferramenta como o Management Studio com um usuário que pode criar tabelas livremente é a receita para acumular tabelas desnecessárias na base de dados. Outro motivo que agrava a situação é a falta de ambientes de desenvolvimento, testes, homologação e produção adequados e sincronizados, pois sem estes recursos os desenvolvedores, infelizmente, acabam tendo que criar objetos em lugares que não são adequados quando precisam fazer seu trabalho e resolver seus problemas.

Bem, posso dizer que abrir a pasta Tables do Management Studio e aguardar um minuto para observar uma lista longa de tabelas não é uma imagem muito bonita de se ver, e a existência de muitas tabelas gera vários malefícios para a saúde do banco e do sistema como um todo.

O primeiro problema causado pelos excesso de tabelas é o espaço em disco. Cada vez que se criar uma tabela com dados, seja com a cláusula INTO ou por outro meio, obrigatoriamente estamos ocupando espaço no banco de dados. Já presenciei situações onde desenvolvedores e DBAs não se preocuparam com isso, pois possuíam espaço em disco sobrando. De qualquer maneira, as tabelas estão ocupando espaço com dados duplicados, algo que incha o banco de dados e torna diversas tarefas mais complicadas, como o backup, recovery, migração, replicação e outras tarefas administrativas.

Cada nova tabela criada no SQL Server armazena metadados. Isso quer dizer que, por exemplo, se criamos uma tabela com duas colunas no SQL teremos, além das linhas da própria tabela, várias linhas nas tabelas de sistemas: uma linha na sysobjects, uma linha na sysindexes, duas linhas na syscolumns, e em outras tabelas que geram overhead no banco de dados. Obviamente, estas linhas de metadados não ocupam tanto espaço quando as próprias linhas das tabelas, mas elas podem acabar prejudicando o uso diário do banco de dados.

Outro impacto negativo é na memória. Se essas tabelas que foram criadas com a cláusula INTO forem acessadas alguma vez, provavelmente o SQL Server vai colocar as suas páginas de dados na memória e, como geralmente estas tabelas não apresentam índices, o uso de memória vai ser excessivo. Apesar de o SQL Server gerenciar esta alocação de memória dinamicamente, ou seja, tirando e colocando páginas de dados conforme o uso e acesso às tabelas, é possível que em algum lugar da memória existam dados de tabelas que não são mais necessários.

Além de não possuir índices, as tabelas criadas com a cláusula INTO também não possuem nenhum tipo de consistência, relacionamentos e garantia de que a informação que está lá é válida e confiável. Isso pode gerar confusão para certos desenvolvedores que não estão a par da criação desta tabelas ao ponto de criarem soluções pensando que o que está no banco de dados é permanente e consistente. Por exemplo: um desenvolvedor pode precisar gerar um relatório complexo e, quando vê que já existe uma tabela com mais ou menos os dados que ele precisa no banco, decide usar a tabela existente gerada a partir da cláusula INTO por outro desenvolvedor. Porém ele não está a par do fato de que esta tabela é temporária, não possui índices, está com dados incompletos e outros problemas. Resultado: uma solução é montada baseada em informação incorretas, gerando diversos problemas quando o usuário for utilizar o relatório.

Outro malefício de tabelas em excesso é na usabilidade das ferramentas. Quando se tem muitas tabelas em um mesmo banco de dados, a interface do Management Studio fica muito mais lenta para carregar os dados necessários às operações que envolvem a listagem das tabelas existentes e outros objetos, chegando ao ponto de demorar tanto que o usuário pode pensar que a ferramenta travou. Praticamente toda a tela ou assistente do Management Studio que precisa saber quais são os objetos do banco de dados fica prejudicada, atrasando e complicando o trabalho dos desenvolvedores e DBAs.

Infelizmente o SQL Server não possui bons recursos para indicar quando foi a última vez que a tabela foi utilizada, isto é, dizer a qual última data em que alguma instrução SELECT leu a tabela. O máximo que pode ser feito é descobrir quando a tabela foi criada, com data e hora. Isso é realizado de diversas formas, como o uso da stored procedure de sistema sp_help, que recebe como parâmetro o nome da tabela. Ou seja, infelizmente é preciso uma análise manual para identificar quem foi o responsável pela criação de cada tabela e se elas podem ser removidas da base de dados ou não.

A criação e definição de políticas para o usuários que possuem o acesso direto ao banco de dados é uma das soluções que podemos adotar para evitar a criação excessiva de tabelas no banco de dados. Esta política deve ser seguida da mesma maneira que se segue a nomenclatura para os objetos do banco de dados.

Uma das regras desta política é a sugestão de utilização de tabelas temporárias (aquelas que começam com #). Este tipo de tabela pode ser criado pela cláusula INTO da instrução SELECT e é automaticamente eliminado quando se fecha a conexão que o criou. Mesmo assim, deve-se evitar duplicar toda a tabela, pois o banco de dados consume recursos preciosos mesmo com tabelas temporárias.

Uma outra idéia interessante é a utilização de views, pois este objeto permite que se filtre o conteúdo de uma tabela, mostrando algumas colunas da tabela originam e apenas as linhas que satisfazem certos filtros da cláusula WHERE. Como a view não copia dados, ela pode substituir a criação de uma tabela a partir da cláusula INTO.

Por fim, recomendo que caso seja necessário a criação de tabelas desta maneira, isto é com a cláusula INTO da instrução SELECT, deve-se estipular um banco de dados ou database schema próprio para elas, uma nomenclatura que as identifique rapidamente e também uma política para a remoção das mesmas, como uma tarefa agendada que dispara um job responsável por apagar todas as tabelas que começam com X no nome a cada duas semanas, por exemplo.

Além disso, o controle de permissões do SQL Server possui diversos recursos para controlar e atribuir o direito de criação de tabelas apenas para quem realmente precisa fazê-lo. Outro recurso útil para o controle é o uso de triggers para comandos DDL, que podem ajudar a rastrear quem são os responsáveis pela criação excessiva de tabelas e como esta criação está sendo feita.

Para terminar, indico que a saúde do banco de dados também está relacionada com os objetos que não são necessariamente utilizados no modelo de dados do sistema. Isso quer dizer que problemas de desempenho, manutenção, consumo de recursos e outros devem analisar o banco de dados como um todo, sempre levando em consideração o que e como os desenvolvedores e DBA podem e estão fazendo diretamente no banco de dados.

Qual a sua opinião?