Todo SGBD usa um conjunto de tabelas para registrar dados sobre os objetos que fazem parte dos bancos de dados. Estes dados são muitas vezes chamados de metadados, ou seja, são dados que descrevem detalhadamente os dados (leia-se ‘objetos’) do ambiente: tabelas, visões, índices, chaves estrangeiras, chaves primárias, procedimentos armazenados, gatilhos, etc.
As tabelas onde se gravam estes metadados costumam ser chamadas de tabelas de sistema ou catálogo. No DB2, quando se cria um novo banco de dados, automaticamente é criado um esquema chamado SYSCAT e mais uma série de tabelas de catálogo (são 151 no total). Para listar todas elas, execute o comando:
db2 list tables for schema syscat
Caso você precise identificar os campos de uma tabela, use o comando:
db2 describe table syscat.NOME_DA_TABELA
De qualquer modo, não precisamos conhecer muitas tabelas do catálogo. A grosso modo, as principais são:
CATÁLOGO | DESCRIÇÃO |
syscat.tables | tabelas do banco, incluindo de usuários e catálogo |
syscat.columns | colunas de cada tabela e seus detalhes |
syscat.views | visões do banco, incluindo de usuários e catálogo |
syscat.indexes | índexes do banco incluindo chaves primárias |
syscat.schemata | esquemas usados no banco |
syscat.triggers | todos os gatilhos, incluindo seus códigos |
syscat.procedures | todos os procedimentos armazenados, incluindo seus códigos |
syscat.functions | todas as funções, incluindo seus códigos |
syscat.references | definições das chaves estrangeiras |
Evidentemente, nunca se deve inserir, apagar ou alterar registros nas tabelas do catálogo. Mas é muito útil ler estes dados, porque eles oferecem muitas possibilidades.
Eu tenho o hábito de consultar o catálogo para criar scripts, principalmente para resolver tarefas repetitivas e tediosas.
Para apresentar a ideia, vou usar um exemplo simples. Preciso obter informações sobre a quantidade de registros de cada uma das tabelas de usuário de um banco de dados. Mas existem vários esquemas de usuário na minha base.
Eu poderia escrever uma consulta de agrupamento para cada uma das tabelas de cada um dos esquemas de usuário e seria algo do tipo:
SELECT ''NOME_SCHEMA''.''NOME_TABELA'' AS TABELA, COUNT(*) FROM NOME_SCHEMA.NOME_TABELA
Isso é fácil de fazer quando se tem meia dúzia de tabelas. Mas num banco de dados real, podemos ter centenas delas. Então a ideia é usar o catálogo para criar a consulta de todas as tabelas ao mesmo tempo, de modo que o resultado mostre uma listagem completa das tabelas e a quantidade de registros.
O truque todo está em entender a declaração SQL que mostrei acima como uma simples cadeia de caracteres. Pensando assim, eu posso então usar uma consulta no catálogo para criar o script que vai fazer a consulta de todas as tabelas ao mesmo tempo.
A ideia parece confusa à primeira vista, mas ela é bem simples. Cada registro da tabela de catálogo syscat.tables informa o nome do esquema e o nome de uma das tabelas do banco. Então, eu faço uma concatenação de cadeias de caracteres de modo a escrever o que mais tarde será usado como uma declaração SQL. Vai ficar algo parecido com a declaração SQL a seguir:
--Exemplo 1 SELECT 'select ''' || T.TABSCHEMA || ''' as esquema, ''' || T.TABNAME || ''' as tabela, count(*) as registros from ' || T.TABSCHEMA || '.' || T.TABNAME || ' union ' AS SENTENCA FROM SYSCAT.TABLES T WHERE TABSCHEMA NOT LIKE 'SYS%'
Vamos analisar esta consulta. O que foi escrito em preto é a consulta que é executada e o que está em azul são os textos complementares para que a cadeia de caracteres gerada tenha a sintaxe correta de uma nova declaração SQL. Note que para escrever uma cadeia de caracteres que contenha uma aspa simples (‘) sou obrigado a escrever duas aspas simples seguidas (‘’) para que o compilador entenda que aquilo é um caractere aspa simples e não o delimitador da cadeia de caracteres.
Para trazer apenas as tabelas de usuário, eu escrevi a cláusula WHERE que filtra os registros em que o nome do esquema não comece com o prefixo SYS, usado pelas tabelas do catálogo.
Eu adicionei a palavra UNION no final da cadeia de caracteres para que toda listagem resultante seja entendida como uma única consulta SQL. Mas isso tem um problema: a última linha da listagem terminará com a palavra UNION e isso não pode acontecer com uma declaração SQL. Veja a listagem a seguir:
SENTENCA
—————————————————————————————————————————-
select ‘DB2INST1’ as esquema, ‘ACT’ as tabela, COUNT(*) as registros from DB2INST1.ACT union
select ‘DB2INST1’ as esquema, ‘ADEFUSR’ as tabela, COUNT(*) as registros from DB2INST1.ADEFUSR union
…
select ‘DB2INST1’ as esquema, ‘VSTAFAC2’ as tabela, COUNT(*) as registros from DB2INST1.VSTAFAC2 union
Observe que, como eu havia dito, eu posso tratar a listagem inteira como uma única declaração SQL, com exceção da palavra UNION no final do último registro. Caso você seja um perfeccionista e não queira ter o trabalho de desprezar esta última palavra quando for copiar a declaração toda, você pode usar a declaração do exemplo 1 como uma subconsulta e depois usar as funções LEFT e LENGTH para descartar os últimos seis caracteres (ou seja a cadeia “ UNION”):
SELECT LEFT(S.sentenca, LENGTH(S.sentenca, 6)) as DECLARACAO FINAL FROM (....) AS S
Copiando o resultado do campo SENTENCA, tenho um script pronto para consultar de uma só vez todas as tabelas de usuários do banco de dados. Na minha base de exemplo, havia 50 tabelas e a sentença criada me permite consultar todas as tabelas de uma só vez. Rodando este script, eu finalmente obtenho a lista que eu desejava no início, informando a quantidade de registros existentes em cada uma das tabelas de usuário.
esquema tabela registros
————————— ————————— ———–
DB2INST1 CATALOG 0
DB2INST1 MEUTESTE 0
DB2INST1 PRODUCTSUPPLIER 2
DB2INST1 SUPPLIERS 2
MEUESQUEMA TABELA1 2
MEUESQUEMA TABELA2 2
DB2INST1 IN_TRAY 3
DB2INST1 INVENTORY 4
…….
Esta ideia pode ser adaptada para virtualmente qualquer SGBD. Eu mesmo já usei este recurso com DB2, SQL SERVER e SYBASE. Basta conhecer as tabelas de sistema do seu SGBD.