Bruker du db2look for å etterligne en database?
On februar 14, 2021 by adminJeg er i ferd med å migrere noen databaser fra iso8859-1 til utf-8. En av databasene inneholder 1000+ tabeller, masse prosedyrer, funksjoner, utløsere, begrensninger osv. Jeg vil automatisere prosessen så mye som mulig siden dette kommer til å skje flere ganger, og i flere tilfeller av systemet. Ideelt sett vil jeg overlate arbeidet til vennene mine Jenkins og Ansible 😉
Min plan var å generere ddl med db2look, men den klarer ikke å generere «objektene» i riktig rekkefølge. Jeg prøvde å kjøre både med og uten flagget -ct (mislykkes av forskjellige grunner) For eksempel:
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;
kjører
db2look -d <db> -e -td @ -ct
genererer feil rekkefølge for indeksen og begrensningen.
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@
Fjerning av -ct fungerer i dette trivielle eksemplet, men mislykkes i selve databasen på grunn av andre avhengigheter.
Jeg forestiller meg at migrering til UTF-8 er en ganske vanlig oppgave, så jeg er nysgjerrig på hva s folk har gjort. De to mulige løsningene jeg ser er å:
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.
Det er klart at det er ulemper med begge disse, overser jeg noen trivielle måter å migrere databasene på?
EDIT: En ekstra observasjon er at så lenge en indeks ikke inneholder noen ekstra attributter sammenlignet med den implisitt opprettede indeksen gjennom en unik / primær nøkkelbegrensning, en QL0605W
advarsel blir hevet. Hvis derimot flere attributter er spesifisert, heves en SQL0601N
-feil. Eksempel:
CREATE UNIQUE INDEX X1 ON T1 (C1, C2) COMPRESS NO INCLUDE NULL KEYS DISALLOW REVERSE SCANS
genererer en advarsel SQL0605W
hvis en lignende indeks opprettes via primærnøkkeluttrykk.
På den annen side genererer en indeks som:
CREATE UNIQUE INDEX X2 ON T2 (C1, C2) INCLUDE (C3) CLUSTER COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS
en feil SQL0601N
. Jeg antar at dette skyldes INCLUDE-klausulen, men kanskje CLUSTER-klausulen vil også forårsake denne oppførselen.
Kommentarer
Svar
For noen år siden overførte vi DB2 fra 9.7 til 10.5. Vi flyttet også databasen til ny maskinvare og implementerte datakomprimering. På grunn av dette bestemte vi oss for å opprette databasen fra bunnen av og eksportere og importere dataene.
Vi brukte db2look og db2move til å gjøre jobben. Imidlertid var det ingen måte å få DDL opprettet av db2look i riktig rekkefølge. Vi måtte dele det genererte skriptet i forskjellige deler for å lage tabeller, lage utløsere, lage indekser osv.
Til slutt ender vi med følgende trinn:
- eksportere eksisterende data
- opprett den nye databasen
- opprett bufferpools og tabellområder
- opprett tabeller
- opprett primærnøkler
- lage indekser
- lage visninger
- laste data
- kjøre reorgs og runstats
- lage lagrede prosedyrer, brukerdefinerte funksjoner og utløsere
Jeg håper det hjelper, selv det er ikke svaret du kanskje hadde forventet.
Kommentarer
- Takk for svaret ditt. Situasjonen er litt mer komplisert (det er for eksempel tabeller som er avhengig av funksjoner og omvendt). Jeg ‘ Jeg oppretter sannsynligvis en parser som sorterer utdataene fra db2look i topologisk rekkefølge ved hjelp av avhengighetstabeller.
Svar
Grov idé om hvordan du får databaseobjekter i riktig rekkefølge. Avhengighetsgrafen er ikke komplett, men ser ut til å oppfylle mine behov.
#!/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))
Nå kan x brukes til å plukke ting i riktig rekkefølge fra db2look. Jeg brukte en triviell parser som leser utdataene fra db2look
i lister. Ved å løkke over x og plukke definisjonen fra høyre bøtte, kan en sortert utgang oppnås. Parseren i seg selv er bare en haug med faste uttrykk og ikke spesielt interessant, men siden uttalelsene er spredt over flere linjer, er en stmt-leser hyggelig å ha:
# 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: Jeg har en parser oppe på https://github.com/lelle1234/Db2Utils . Det er på ingen måte komplett, men fungerte for mine behov.
Det er også en indeksrådgiver der som prøver å komme med et optimalt sett med indekser for et gitt spørsmål og en database.
Svar
Jeg har brukt følgende tilnærming for å kopiere produksjonsskjema for å teste servere for utviklere. Trikset er å ignorere feil under individuelle løp og sammenligne skjemaet til slutt for å sikre at alt blir gjenskapt riktig. Jeg vet at det ikke er en ren tilnærming. Jeg har gjort dette mot skjema med over 1000 tabeller, 500 funksjoner med avhengigheter, begrensninger osv. Du kan automatisere trinnene ved hjelp av skallskript. Det krever mye mindre innsats i forhold til å skrive parser og teste den.
Trinn 1 Bruk skriptet til å trekke ut skjema fra produksjonsserveren i spesifikk rekkefølge (db2look for tabellområder først, db2look for tabeller neste, osv.). Ved å bruke spesifikk rekkefølge reduseres antall iterasjoner for trinn 2 & 3.
Trinn 2 Kjør utdata fra trinn 1 på testserver
Trinn 3 Pakk ut skjema fra testserver og sammenlign det med skjema fra produksjonsserver
Trinn 4 Gjenta trinn 2 og 3, til begge skjemaene er synkronisert.
AUTO_REVAL
databasekonfigurasjonsparameter for åDEFERRED_FORCE
løse problemet ditt?db2 +c -s ...
) slik at jeg trygt kan si at alt er gjenskapt riktig.