Använder du db2look för att efterlikna en databas?
On februari 14, 2021 by adminJag är i färd med att migrera vissa databaser från iso8859-1 till utf-8. En av databaserna innehåller mer än 1000 tabeller, massor av procedurer, funktioner, utlösare, begränsningar etc. Jag vill automatisera processen så mycket som möjligt eftersom detta kommer att hända flera gånger, och i flera fall av systemet, helst skulle jag vilja överlämna arbetet till mina vänner Jenkins och Ansible 😉
Min plan var att generera ddl med db2look, men det går inte att generera ”objekten” i rätt ordning. Jag försökte köra både med och utan flaggan -ct (misslyckas av olika skäl) Till exempel:
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;
kör
db2look -d <db> -e -td @ -ct
genererar fel ordning för indexet och begränsningen.
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@
Att ta bort -ct fungerar i detta triviala exempel, men misslyckas i den faktiska databasen på grund av andra beroenden.
Jag föreställer mig att migrera till UTF-8 är en ganska vanlig uppgift så jag är nyfiken på vad s människor har gjort. De två möjliga lösningarna jag ser är att:
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 är uppenbart att det finns nackdelar med båda dessa, ser jag på något trivialt sätt att migrera databaserna?
EDIT: En extra observation är att så länge ett index inte innehåller några extra attribut jämfört med det implicit skapade indexet genom en unik / primär nyckelbegränsning en QL0605W
varning höjs. Om å andra sidan ytterligare attribut anges anges ett SQL0601N
-fel. Exempel:
CREATE UNIQUE INDEX X1 ON T1 (C1, C2) COMPRESS NO INCLUDE NULL KEYS DISALLOW REVERSE SCANS
genererar en varning SQL0605W
om ett liknande index skapas via primär nyckeluttalande.
Å andra sidan genererar ett index som:
CREATE UNIQUE INDEX X2 ON T2 (C1, C2) INCLUDE (C3) CLUSTER COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS
ett fel SQL0601N
. Jag antar att detta beror på INCLUDE-klausulen, men kanske kommer CLUSTER-klausulen att orsaka detta beteende också.
Kommentarer
Svar
För några år sedan migrerade vi vår DB2 från 9.7 till 10.5. Vi flyttade också databasen till ny hårdvara och implementerade datakomprimering. På grund av detta bestämde vi oss för att skapa databasen från grunden och exportera och importera data.
Vi använde db2look och db2move för att göra jobbet. Det fanns dock inget sätt att få DDL skapad av db2look i rätt ordning. Vi var tvungna att dela upp det genererade skriptet i olika delar för att skapa tabeller, skapa triggers, skapa index etc.
Slutligen slutar vi med följande steg:
- exportera befintliga data
- skapa den nya databasen
- skapa buffertpooler och tabellutrymmen
- skapa tabeller
- skapa primära nycklar
- skapa index
- skapa vyer
- ladda data
- kör reorgs och runstats
- skapa lagrade procedurer, användardefinierade funktioner och utlösare
Jag hoppas att det hjälper, även det är inte svaret du kanske hade förväntat dig.
Kommentarer
- Tack för ditt svar. Situationen är lite mer komplicerad (det finns till exempel tabeller som är beroende av funktioner och vice versa). Jag ’ Jag kommer förmodligen att skapa en parser som sorterar utdata från db2look i topologisk ordning med hjälp av beroendestabeller.
Svar
Grov idé om hur man får databasobjekt i rätt ordning. Beroendediagrammet är inte komplett men verkar uppfylla mina 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))
Nu kan x användas för att välja saker i rätt ordning från db2look. Jag använde en trivial parser som läser utdata från db2look
i listor. Genom att slinga över x och välja definitionen från höger skopa kan en sorterad utgång uppnås. Parsern i sig är bara en massa reguljära uttryck och inte särskilt intressant, men eftersom uttalandena är spridda över flera rader är en stmt-läsare trevlig att 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: Jag har en parser vid https://github.com/lelle1234/Db2Utils . Det är inte alls komplett men fungerade för mina behov.
Det finns också en indexrådgivare där som försöker komma med en optimal uppsättning index för en viss fråga och en databas.
Svar
Jag har använt följande tillvägagångssätt för att kopiera produktionsschema för att testa servrar för utvecklare. Trick är att ignorera fel under enskilda körningar och jämföra schemat i slutet för att se till att allt återskapas korrekt. Jag vet att det inte är ett rent tillvägagångssätt. Jag har gjort detta mot schema med över 1000 tabeller, 500 funktioner med beroenden, begränsningar osv. Du kan automatisera stegen med hjälp av skalskript. Det kräver mycket mindre ansträngning jämfört med att skriva parser och testa den.
Steg 1 Använd skript för att extrahera schema från produktionsservern i specifik ordning (db2look för tabellutrymmen först, db2look för tabeller nästa, etc ..). Genom att använda en specifik ordning minskar antalet iterationer för steg 2 & 3.
Steg 2 Kör utdata från steg 1 på testserver
Steg 3 Extrahera schema från testserver och jämför det med schema från produktionsserver
Steg 4 Upprepa steg 2 och 3 tills båda scheman är synkroniserade.
AUTO_REVAL
konfigurationsparameter för databas för attDEFERRED_FORCE
lösa ditt problem?db2 +c -s ...
) så att jag säkert kan säga att allt återskapas korrekt.