Usando db2look para imitar um banco de dados?
On Fevereiro 14, 2021 by adminEstou no processo de migração de alguns bancos de dados de iso8859-1 para utf-8. Um dos bancos de dados contém mais de 1000 tabelas, muitos procedimentos, funções, gatilhos, restrições etc. Eu gostaria de automatizar o processo o máximo possível, pois isso vai acontecer várias vezes e para várias instâncias do sistema. O ideal é passar o trabalho para meus amigos Jenkins e Ansible 😉
Meu plano era gerar o ddl com db2look, mas ele falha ao gerar os “objetos” na ordem correta. Tentei executar com e sem o sinalizador -ct (falha por motivos diferentes) . Por exemplo:
connect to <db>; create table t1 (x int not null); create unique index t1pk on t1 (x); alter table t1 add constraint t1pk primary key (x); connect reset;
executando
db2look -d <db> -e -td @ -ct
gera a ordem errada para o índice e a restrição.
CREATE TABLE "DB2INST1"."T1" ( "X" INTEGER NOT NULL ) IN "USERSPACE1" ORGANIZE BY ROW@ ALTER TABLE "DB2INST1"."T1" ADD CONSTRAINT "T1PK" PRIMARY KEY ("X")@ CREATE UNIQUE INDEX "DB2INST1"."T1PK" ON "DB2INST1"."T1" ("X" ASC) COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS@
A remoção de -ct funciona neste exemplo trivial, mas falha no banco de dados real devido a outras dependências.
Imagino que migrar para UTF-8 seja uma tarefa bastante comum, então estou curioso sobre o que p as pessoas fizeram. As duas soluções possíveis que vejo são:
a) write a parser that inspects the catalog for database objects and sort them topologically using dependency tables. b) write a parser that reads the output from db2look, identify each object and sort them topologically using dependency tables.
Claramente, há desvantagens em ambas, estou negligenciando alguma maneira trivial de migrar os bancos de dados?
EDITAR: Uma observação adicional é que, contanto que um índice não contenha nenhum atributo extra em comparação com o índice criado implicitamente por meio de uma restrição de chave primária / exclusiva, um QL0605W
aviso é gerado. Se, por outro lado, atributos adicionais forem especificados, um erro SQL0601N
será gerado. Exemplo:
CREATE UNIQUE INDEX X1 ON T1 (C1, C2) COMPRESS NO INCLUDE NULL KEYS DISALLOW REVERSE SCANS
gera um aviso SQL0605W
se um índice semelhante for criado por meio da instrução de chave primária.
Por outro lado, um índice como:
CREATE UNIQUE INDEX X2 ON T2 (C1, C2) INCLUDE (C3) CLUSTER COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS
gera um erro SQL0601N
. Presumo que isso se deva à cláusula INCLUDE, mas talvez a cláusula CLUSTER também cause esse comportamento.
Comentários
Resposta
Há alguns anos, migramos nosso DB2 de 9,7 para 10,5. Também movemos o banco de dados para um novo hardware e implementamos a compactação de dados. Por isso decidimos criar o banco de dados do zero e exportar e importar os dados.
Usamos db2look e db2move para fazer o trabalho. No entanto, não havia como obter o DDL criado por db2look na ordem correta. Tivemos que dividir o script gerado em partes diferentes para criar tabelas, criar gatilhos, criar índices, etc.
Finalmente, terminamos com as seguintes etapas:
- exportar o dados existentes
- criar o novo banco de dados
- criar bufferpools e espaços de tabela
- criar tabelas
- criar chaves primárias
- criar índices
- criar visualizações
- carregar dados
- executar reorgs e runstats
- criar procedimentos armazenados, funções definidas pelo usuário e gatilhos
Espero que ajude, mesmo não sendo a resposta que você esperava.
Comentários
- Obrigado pela resposta. A situação é um pouco mais complicada (existem, por exemplo, tabelas que dependem de funções e vice-versa). Eu ‘ provavelmente criarei um analisador que classifica a saída do db2look em ordem topológica usando tabelas de dependência.
Resposta
Idéia aproximada sobre como obter os objetos de banco de dados na ordem correta. O gráfico de dependência não está completo, mas parece atender às minhas necessidades.
#!/usr/bin/python3 import ibm_db import ibm_db_dbi from toposort import toposort, toposort_flatten cfg = ... conn = ibm_db.connect("DATABASE=%s;HOSTNAME=%s;PORT=50000;PROTOCOL=TCPIP;UID=%s; PWD=%s" % cfg,"","") find_edges = """ select * from ( SELECT "CONSTRAINT" as type, CONSTNAME, TABSCHEMA, TABNAME, BTYPE, "N/A", BSCHEMA, BNAME FROM SYSCAT.CONSTDEP WHERE TABSCHEMA NOT LIKE "SYS%" AND BSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "I", "N/A", D.INDSCHEMA, D.INDNAME, D.BTYPE, "N/A", D.BSCHEMA, D.BNAME FROM SYSCAT.INDEXDEP D JOIN SYSCAT.INDEXES I ON D.INDSCHEMA = I.INDSCHEMA AND D.INDNAME = I.INDNAME WHERE I.TABSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "I", "N/A", I.INDSCHEMA, I.INDNAME, "T", "N/A", I.TABSCHEMA, I.TABNAME FROM SYSCAT.INDEXES I WHERE I.TABSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "F", "N/A", R1.ROUTINESCHEMA, R1.ROUTINENAME, D.BTYPE, "N/A" , COALESCE(R2.ROUTINESCHEMA, D.BSCHEMA), COALESCE(R2.ROUTINENAME, D.BNAME) FROM SYSCAT.ROUTINEDEP D JOIN SYSCAT.ROUTINES R1 ON D.ROUTINESCHEMA = R1.ROUTINESCHEMA AND D.SPECIFICNAME = R1.SPECIFICNAME LEFT JOIN SYSCAT.ROUTINES R2 ON D.BSCHEMA = R2.ROUTINESCHEMA AND D.BNAME = R2.SPECIFICNAME AND D.BTYPE = "F" WHERE D.ROUTINESCHEMA NOT LIKE "SYS%" AND D.BSCHEMA NOT LIKE "SYS%" AND D.BTYPE <> "K" UNION ALL SELECT "T", "N/A", TABSCHEMA, TABNAME, BTYPE, "N/A", BSCHEMA, BNAME FROM SYSCAT.TABDEP WHERE TABSCHEMA NOT LIKE "SYS%" AND BSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "X", "N/A", TRIGSCHEMA, TRIGNAME, BTYPE, "N/A", BSCHEMA, BNAME FROM SYSCAT.TRIGDEP WHERE TRIGSCHEMA NOT LIKE "SYS%" AND BSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "T", "N/A", TABSCHEMA, TABNAME, "T", "N/A", REFTABSCHEMA, REFTABNAME FROM SYSCAT.REFERENCES WHERE TABSCHEMA NOT LIKE "SYS%" ORDER BY 3,4 ) """ sedges = ibm_db.prepare(conn, find_edges) edges = {} ibm_db.execute(sedges, ()) lastnode = None tpl = ibm_db.fetch_tuple(sedges) while tpl: n1 = (tpl[0], tpl[1], tpl[2], tpl[3]) n2 = (tpl[4], tpl[5], tpl[6], tpl[7]) if lastnode == n1: edges[n1].add(n2) else: # print("new") edges[n1] = set() edges[n1].add(n2) lastnode = n1 tpl = ibm_db.fetch_tuple(sedges) x = list(toposort_flatten(edges))
Agora, x pode ser usado para escolher as coisas na ordem correta do db2look. Usei um analisador trivial que lê a saída de db2look
em listas. Fazendo um loop sobre x e escolhendo a definição do balde certo, uma saída classificada pode ser alcançada. O analisador em si é apenas um monte de expressões regulares e não é particularmente interessante, mas como as instruções estão espalhadas por várias linhas, é bom ter um leitor de stmt:
# helper for reading stmt by stmt def myreadlines(f, newline): buf = "" while True: while newline in buf: pos = buf.index(newline) yield buf[:pos] buf = buf[pos + len(newline):] chunk = f.read(4096) if not chunk: yield buf break buf += chunk
EDITAR: Tenho um analisador em https://github.com/lelle1234/Db2Utils . Não está de forma alguma completo, mas funcionou para minhas necessidades.
Há também um consultor de índice lá que tenta chegar a um conjunto ótimo de índices para uma determinada consulta e um banco de dados.
Resposta
Usei a seguinte abordagem para copiar o esquema de produção para testar servidores para desenvolvedores. O truque é ignorar erros durante execuções individuais e comparar o esquema no final para garantir que tudo seja recriado corretamente. Eu sei que não é uma abordagem limpa. Eu fiz isso contra o esquema com mais de 1000 tabelas, 500 funções com dependências, restrições, etc. Você pode automatizar as etapas usando o script de shell. Requer muito menos esforço em comparação com escrever o analisador e testá-lo.
Etapa 1 Use o script para extrair o esquema do servidor de produção em uma ordem específica (db2look para espaços de tabela primeiro, db2look para tabelas depois, etc.) O uso de ordem específica reduz o número de iterações para a etapa 2 & 3.
Etapa 2 Execute a saída da etapa 1 no servidor de teste
Etapa 3 Extraia o esquema do servidor de teste e compare-o com o esquema do servidor de produção
Etapa 4 Repita as etapas 2 e 3, até que ambos os esquemas estejam sincronizados.
AUTO_REVAL
paraDEFERRED_FORCE
resolver seu problema?db2 +c -s ...
) para poder dizer com segurança que tudo foi recriado corretamente.