Db2

使用 db2look 模擬數據庫?

  • January 30, 2020

我正在將一些數據庫從 iso8859-1 遷移到 utf-8。其中一個數據庫包含 1000 多個表、大量程序、函式、觸發器、約束等。我想盡可能地自動化該過程,因為這將發生多次,並且適用於系統的多個實例。理想情況下,我想把工作交給我的朋友 Jenkins 和 Ansible ;-)

我的計劃是使用 db2look 生成 ddl,但它無法以正確的順序生成“對象”。我嘗試在有和沒有 -ct 標誌的情況下執行(由於不同的原因而失敗)。例如:

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;

跑步

db2look -d <db> -e -td @ -ct

為索引和約束生成錯誤的順序。

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@

刪除 -ct 在這個簡單的範例中有效,但在實際數據庫中由於其他依賴關係而失敗。

我想遷移到 UTF-8 是一項非常常見的任務,所以我很好奇人們做了什麼。我看到的兩種可能的解決方案是:

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.

顯然這兩者都有缺點,我是否忽略了一些遷移數據庫的簡單方法?

編輯:另一個觀察結果是,只要與通過唯一/主鍵約束隱式創建的索引相比,索引不包含任何額外屬性,QL0605W就會引發警告。另一方面,如果指定了附加屬性,SQL0601N則會引發錯誤。例子:

CREATE UNIQUE INDEX X1 ON T1 (C1, C2) 
COMPRESS NO 
INCLUDE NULL KEYS 
DISALLOW REVERSE SCANS

SQL0605W如果通過主鍵語句創建了類似的索引,則會生成警告。

另一方面,索引如下:

CREATE UNIQUE INDEX X2 ON T2 (C1, C2) 
INCLUDE (C3) 
CLUSTER 
COMPRESS NO 
INCLUDE NULL KEYS 
ALLOW REVERSE SCANS

產生錯誤SQL0601N。我認為這是由於 INCLUDE 子句造成的,但也許 CLUSTER 子句也會導致這種行為。

關於如何以正確順序獲取數據庫對象的粗略想法。依賴關係圖不完整,但似乎滿足了我的需求。

#!/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))

現在,x 可用於從 db2look 中以正確的順序挑選內容。我使用了一個簡單的解析器,它將輸出讀取db2look到列表中。通過循環 x 並從正確的儲存桶中選擇定義,可以實現排序輸出。解析器本身只是一堆正則表達式,並不是特別有趣,但是由於語句分佈在幾行中,因此最好有一個 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

編輯:我在https://github.com/lelle1234/Db2Utils有一個解析器。它絕不是完整的,但可以滿足我的需要。

那裡還有一個索引顧問,它試圖為給定的查詢和數據庫提供一組最佳索引。

幾年前,我們將 DB2 從 9.7 遷移到 10.5。我們還將數據庫移至新硬體並實施了數據壓縮。因此,我們決定從頭開始創建數據庫並導出和導入數據。

我們使用 db2look 和 db2move 來完成這項工作。但是,無法以正確的順序獲取 db2look 創建的 DDL。我們不得不將生成的腳本分成不同的部分來創建表、創建觸發器、創建索引等。

最後我們完成以下步驟:

  • 導出現有數據
  • 創建新數據庫
  • 創建緩衝池和表空間
  • 創建表
  • 創建主鍵
  • 創建索引
  • 創建視圖
  • 載入數據
  • 執行重組和執行統計
  • 創建儲存過程、使用者定義的函式和触發器

我希望它有所幫助,即使它不是您可能期望的答案。

引用自:https://dba.stackexchange.com/questions/169185