¿Utiliza db2look para imitar una base de datos?
On febrero 14, 2021 by adminEstoy en el proceso de migrar algunas bases de datos de iso8859-1 a utf-8. Una de las bases de datos contiene más de 1000 tablas, montones de procedimientos, funciones, disparadores, restricciones, etc. Me gustaría automatizar el proceso tanto como sea posible ya que esto va a suceder varias veces y para varias instancias del sistema. Lo ideal sería entregar el trabajo a mis amigos Jenkins y Ansible 😉
Mi plan era generar el ddl con db2look, pero falla al generar los «objetos» en el orden correcto. Intenté ejecutar ambos con y sin el indicador -ct (falla por diferentes razones) . Por ejemplo:
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;
en ejecución
db2look -d <db> -e -td @ -ct
genera el orden incorrecto para el índice. y la restricción.
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@
La eliminación de -ct funciona en este ejemplo trivial, pero falla en la base de datos real debido a otras dependencias.
Me imagino que migrar a UTF-8 es una tarea bastante común, así que estoy curioso sobre lo que p la gente lo ha hecho. Las dos posibles soluciones que veo son:
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 hay inconvenientes con ambos, ¿estoy pasando por alto alguna forma trivial de migrar las bases de datos?
EDITAR: Una observación adicional es que mientras un índice no contenga ningún atributo adicional en comparación con el índice creado implícitamente a través de una restricción de clave única / primaria, una QL0605W
advertencia es elevado. Si, por otro lado, se especifican atributos adicionales, se genera un error SQL0601N
. Ejemplo:
CREATE UNIQUE INDEX X1 ON T1 (C1, C2) COMPRESS NO INCLUDE NULL KEYS DISALLOW REVERSE SCANS
genera una advertencia SQL0605W
si se crea un índice similar mediante una declaración de clave primaria.
Por otro lado, un índice como:
CREATE UNIQUE INDEX X2 ON T2 (C1, C2) INCLUDE (C3) CLUSTER COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS
genera un error SQL0601N
. Supongo que esto se debe a la cláusula INCLUDE, pero tal vez la cláusula CLUSTER también cause este comportamiento.
Comentarios
Respuesta
Hace unos años migramos nuestro DB2 de 9.7 a 10.5. También trasladamos la base de datos a un nuevo hardware e implementamos la compresión de datos. Debido a esto, decidimos crear la base de datos desde cero y exportar e importar los datos.
Usamos db2look y db2move para hacer el trabajo. Sin embargo, no había forma de obtener el DDL creado por db2look en el orden correcto. Tuvimos que dividir el script generado en diferentes partes para crear tablas, crear disparadores, crear índices, etc.
Finalmente terminamos con los siguientes pasos:
- exportar el datos existentes
- crear la nueva base de datos
- crear agrupaciones de búfer y espacios de tabla
- crear tablas
- crear claves primarias
- crear índices
- crear vistas
- cargar datos
- ejecutar reorgs y runstats
- crear procedimientos almacenados, funciones definidas por el usuario y activadores
Espero que te ayude, aunque no sea la respuesta que esperabas.
Comentarios
- Gracias por tu respuesta. La situación es un poco más complicada (hay por ejemplo tablas que dependen de funciones y viceversa). Yo ‘ probablemente crearé un analizador que clasifique la salida de db2look en orden topológico usando tablas de dependencia.
Respuesta
Una idea aproximada de cómo colocar los objetos de la base de datos en el orden correcto. El gráfico de dependencia no está completo pero parece satisfacer mis necesidades.
#!/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))
Ahora, x se puede usar para seleccionar cosas en el orden correcto desde db2look. Usé un analizador trivial que lee el resultado de db2look
en listas. Al realizar un bucle sobre x y seleccionar la definición del grupo correcto, se puede lograr una salida ordenada. El analizador en sí mismo es solo un montón de expresiones regulares y no es particularmente interesante, pero dado que las declaraciones se distribuyen en varias líneas, es bueno tener un lector 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: Tengo un analizador en https://github.com/lelle1234/Db2Utils . De ninguna manera está completo, pero funcionó para mis necesidades.
También hay un asesor de índices que intenta generar un conjunto óptimo de índices para una consulta y una base de datos determinadas.
Respuesta
He utilizado el siguiente enfoque para copiar el esquema de producción para probar servidores para desarrolladores. El truco consiste en ignorar los errores durante las ejecuciones individuales y comparar el esquema al final para asegurarse de que todo se vuelva a crear correctamente. Sé que no es un enfoque limpio. He hecho esto contra el esquema con más de 1000 tablas, 500 funciones con dependencias, restricciones, etc. Puede automatizar los pasos usando un script de shell. Requiere mucho menos esfuerzo en comparación con escribir un analizador y probarlo.
Paso 1 Utilice un script para extraer el esquema del servidor de producción en un orden específico (primero db2look para espacios de tabla, luego db2look para tablas, etc.). El uso de un orden específico reduce el número de iteraciones para el paso 2 & 3.
Paso 2 Ejecute el resultado del paso 1 en el servidor de prueba
Paso 3 Extraiga el esquema del servidor de prueba y compárelo con el esquema del servidor de producción
Paso 4 Repita los pasos 2 y 3, hasta que ambos esquemas estén sincronizados.
AUTO_REVAL
paraDEFERRED_FORCE
resolver su problema?db2 +c -s ...
) para poder decir con seguridad que todo se ha recreado correctamente.