Folosiți db2look pentru a imita o bază de date?
On februarie 14, 2021 by adminSunt „în procesul de migrare a unor baze de date de la iso8859-1 la utf-8. Una dintre bazele de date conține peste 1000 de tabele, o mulțime de proceduri, funcții, declanșatoare, constrângeri etc. Aș dori să automatizez procesul cât mai mult posibil, deoarece acest lucru se va întâmpla de mai multe ori și pentru mai multe instanțe ale sistemului. În mod ideal aș dori să predez lucrarea prietenilor mei Jenkins și Ansible 😉
Planul meu a fost să generez ddl cu db2look, dar nu reușește să genereze „obiectele” în ordinea corectă. Am încercat să rulez atât cu și fără semnalizatorul -ct (eșuează din diferite motive) De exemplu:
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;
rulează
db2look -d <db> -e -td @ -ct
generează ordinea greșită a indexului și constrângerea.
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@
Eliminarea -ct funcționează în acest exemplu banal, dar nu reușește în baza de date reală din cauza altor dependențe.
Imaginez că migrarea către UTF-8 este o sarcină destul de obișnuită, așa că sunt curios despre ce pag oamenii au făcut-o. Cele două soluții posibile pe care le văd sunt:
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.
În mod clar, există dezavantaje cu ambele, nu trec cu vederea un mod banal de a migra bazele de date?
EDIT: O observație suplimentară este că, atâta timp cât un index nu conține atribute suplimentare în comparație cu indexul creat implicit printr-o constrângere cheie unică / primară un avertisment QL0605W
este crescut. Dacă pe de altă parte sunt specificate atribute suplimentare, se generează o eroare SQL0601N
. Exemplu:
CREATE UNIQUE INDEX X1 ON T1 (C1, C2) COMPRESS NO INCLUDE NULL KEYS DISALLOW REVERSE SCANS
generează un avertisment SQL0605W
dacă se creează un index similar prin instrucțiunea cheii primare.
Pe de altă parte, un index precum:
CREATE UNIQUE INDEX X2 ON T2 (C1, C2) INCLUDE (C3) CLUSTER COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS
generează o eroare SQL0601N
. Presupun că acest lucru se datorează clauzei INCLUDE, dar poate clauza CLUSTER va provoca și acest comportament.
Comentarii
Răspuns
Acum câțiva ani ne-am migrat DB2 de la 9.7 la 10.5. De asemenea, am mutat baza de date pe hardware nou și am implementat compresia datelor. Din această cauză am decis să creăm baza de date de la zero și să exportăm și să importăm datele.
Am folosit db2look și db2move pentru a face treaba. Cu toate acestea, nu a existat nicio modalitate de a obține DDL creat de db2look în ordinea corectă. A trebuit să împărțim scriptul generat în diferite părți pentru crearea tabelelor, crearea declanșatoarelor, crearea indexurilor etc.
În cele din urmă vom termina cu următorii pași:
- exportăm date existente
- creați noua bază de date
- creați tampoane și spații de tabel
- creați tabele
- creați chei primare
- creați indexuri
- creați vizualizări
- încărcați date
- executați reorgs și runstats
- creați proceduri stocate, funcții definite de utilizator și declanșatoare
Sper că vă va ajuta, chiar și nu este răspunsul la care v-ați fi așteptat.
Comentarii
- Vă mulțumim pentru răspuns. Situația este puțin mai complicată (există, de exemplu, tabele care sunt dependente de funcții și invers). ‘ probabil voi crea un parser care sortează ieșirea din db2look în ordine topologică folosind tabele de dependență.
Răspuns
Idee generale despre cum să obțineți obiectele bazei de date în ordinea corectă. Graficul dependenței nu este complet, dar pare să-mi îndeplinească nevoile.
#!/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))
Acum, x poate fi folosit pentru a alege lucrurile în ordinea corectă din db2look. Am folosit un parser banal care citește ieșirea din db2look
în liste. Făcând o buclă peste x și alegând definiția din cupa dreaptă, se poate obține o ieșire sortată. Analizorul în sine este doar o grămadă de expresii regulate și nu este deosebit de interesant, dar, deoarece declarațiile sunt răspândite pe mai multe linii, un cititor stmt este plăcut să aibă:
# 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
EDIT: Am un analizor la https://github.com/lelle1234/Db2Utils . Nu este în niciun caz complet, dar a funcționat pentru nevoile mele.
Există, de asemenea, un consilier de indexare care încearcă să vină cu un set optim de indici pentru o anumită interogare și o bază de date.
Răspuns
Am folosit următoarea abordare pentru a copia schema de producție pe serverele de testare pentru dezvoltatori. Trucul este de a ignora erorile în timpul rulărilor individuale și de a compara schema la final pentru a vă asigura că totul este recreat corect. Știu că nu este o abordare curată. Am făcut acest lucru împotriva schemei cu peste 1000 de tabele, 500 de funcții cu dependențe, constrângeri etc. Puteți automatiza pașii folosind scriptul shell. Necesită mult mai puțin efort comparativ cu scrierea parserului și testarea acestuia.
Pasul 1 Utilizați scriptul pentru a extrage schema de pe serverul de producție în ordine specifică (db2look pentru tablespaces mai întâi, db2look pentru tabele următoare etc.) Utilizarea unei comenzi specifice reduce numărul de iterații pentru pasul 2 & 3.
Pasul 2 Executați ieșirea de la pasul 1 pe serverul de testare
Pasul 3 Extrageți schema de pe serverul de testare și comparați-o cu schema de pe serverul de producție
Pasul 4 Repetați pașii 2 și 3, până când ambele scheme sunt sincronizate.
AUTO_REVAL
pentru aDEFERRED_FORCE
rezolva problema dvs.?db2 +c -s ...
), astfel încât să pot spune cu siguranță că totul este recreat corect.