Db2:データベースをLoadで全部移行してくれ

# Loadの考慮点
[前回](https://qiita.com/tsunenorikan/items/be1b3c70aafb4f553c8d "Db2:データベースをLoadで全部移行してくれ")は以下を考慮してデータベースをExportをしました。

- レコード長が32KBを超える
- ラージオブジェクト(LOB)がある

前回の記事：[Db2:データベースをLoadで全部移行してくれ](https://qiita.com/tsunenorikan/items/be1b3c70aafb4f553c8d "前回")

今回は以下を考慮してExportしたファイルをデータベースにLoadします。

- 自動生成列(GENERATED ALWAYS AS IDENTITY)がある

# 環境
Windows Server 2008 → 2016
Db2 Windows V10.1 → V11.5

# Loadするやり方

Importは使わず、パフォーマンスに優れるLoadコマンドを使います。

- 自動生成列があるテーブルは"modified by identityoverride"を追加
- 自動生成列なしと自動生成列ありで処理を分割
- Load後、自動生成列のカウンターを修正

Loadではトリガーは起動しませんが後作業でImportをするなら、トリガーをに一旦ドロップし、作業完了後に再定義することを勧めます。

#### 自動生成列なしテーブルをLoadするSQL

```sql:load_AllTable_without_ALWAYS.ddl
select    current date as date, current time as time    from sysibm.sysdummy1          ;

load from TACCTL.ixf                    of ixf  replace into TACCTL                    ;
select    current date as date, current time as time    from sysibm.sysdummy1          ;
load from TADLDT.ixf                    of ixf  replace into TADLDT                    ;
select    current date as date, current time as time    from sysibm.sysdummy1          ;
```
コマンドでシステムテーブルから自動生成列なしテーブルをLoadするSQLを生成します。
db2 -txf load_AllTable_without_ALWAYS.gen -z load_AllTable_without_ALWAYS.ddl
<details><summary> 自動生成列なしLoadを生成するSQL（左の三角をクリックすると展開）</summary><div>

```sql:load_AllTable_without_ALWAYS.gen
values( 'select    current date as date, current time as time    from sysibm.sysdummy1          ;'   )  ;

select  'load from '
    ||  substr(concat(rtrim(TABNAME),'.ixf                              '),1,29)
    ||  ' of ixf  replace into '  
    ||  substr(TABNAME,1,25) 
    ||  ' ;'  
    ||  chr(10)
    ||  'select    current date as date, current time as time    from sysibm.sysdummy1          ;'  
  from SYSCAT.TABLES 
    where not exists ( select * from SYSIBM.SYSCOLUMNS 
                                  where TABNAME = TBNAME
                                    and GENERATED = 'A' )
      and TYPE = 'T' 
      and TABSCHEMA = 'DB2ADMIN' 
      and TABNAME not like  ('ADVISE_%')
      and TABNAME not like  ('EXPLAIN_%')
      and TABNAME not in    ('LOCKEVMON','OBJECT_METRICS') 
    order by TABSCHEMA, TABNAME ;
```

</div></details>

#### 自動生成列ありテーブルをLoadするSQL

```sql:load_AllTable_only_ALWAYS.ddl
select    current date as date, current time as time    from sysibm.sysdummy1          ;

load from TC83TRGWT2_X2.ixf             of ixf  modified by identityoverride replace into TC83TRGWT2_X2             ;
select    current date as date, current time as time    from sysibm.sysdummy1          ;
load from TGW91UKE.ixf                  of ixf  modified by identityoverride replace into TGW91UKE                  ;
select    current date as date, current time as time    from sysibm.sysdummy1          ;
```

"modified by identityoverride"を追加することで自動生成列の値をそのままLoadします。

コマンドでシステムテーブルから自動生成列ありテーブルをLoadするSQLを生成します。
db2 -txf load_AllTable_only_ALWAYS.gen -z load_AllTable_only_ALWAYS.ddl
<details><summary> 自動生成列ありLoadを生成するSQL（左の三角をクリックすると展開）</summary><div>

```sql:load_AllTable_only_ALWAYS.gen
values( 'select    current date as date, current time as time    from sysibm.sysdummy1          ;'   )  ;

select  'load from '
    ||  substr(concat(rtrim(TABNAME),'.ixf                              '),1,29)
    ||  ' of ixf  modified by identityoverride replace into '  
    ||  substr(TABNAME,1,25) 
    ||  ' ;'  
    ||  chr(10)
    ||  'select    current date as date, current time as time    from sysibm.sysdummy1          ;'  
  from SYSCAT.TABLES 
    where exists ( select * from SYSIBM.SYSCOLUMNS 
                                  where TABNAME = TBNAME
                                    and GENERATED = 'A' )
      and TYPE = 'T' 
      and TABSCHEMA = 'DB2ADMIN' 
      and TABNAME not like  ('ADVISE_%')
      and TABNAME not like  ('EXPLAIN_%')
      and TABNAME not in    ('LOCKEVMON','OBJECT_METRICS') 
    order by TABSCHEMA, TABNAME ;
```
</div></details>

# Loadの前処理
データベース構成パラメーターの変更

```sql:02_cfg_log_off.log
update db cfg for QIITA01 using LOGSECOND     50  ;
update db cfg for QIITA01 using LOGPRIMARY    100 ;
update db cfg for QIITA01 using MIRRORLOGPATH ""  ;
update db cfg for QIITA01 using LOGARCHMETH1  off ;
update db cfg for QIITA01 using LOGARCHMETH2  off ;
update db cfg for QIITA01 using FAILARCHPATH  ""  ;
update db cfg for QIITA01 using LOGINDEXBUILD off ;
```
途中でバックアップを要求されないようアーカイブログを無効にしておきます。

# Loadの実行
コマンドでLoadを実行します。
db2- tvf load_AllTable_without_ALWAYS.ddl -z load_AllTable_without_ALWAYS.log
db2 -tvf Load_AllTable_only_ALWAYS.ddl -z Load_AllTable_only_ALWAYS.log

# Loadの後処理
自動生成列のカウンターを最大値+1に更新します。
IBM ウェブサイト：[DB2: 自動生成列の値の確認方法](https://www.ibm.com/support/pages/db2-%E8%87%AA%E5%8B%95%E7%94%9F%E6%88%90%E5%88%97%E3%81%AE%E5%80%A4%E3%81%AE%E7%A2%BA%E8%AA%8D%E6%96%B9%E6%B3%95 "IBM")

- データベースバックアップ
- Runstats（テーブル統計情報の更新）
- 自動生成列のカウンターを更新
- データベース構成パラメーターを元に戻す

#### 自動生成列のカウンターを更新するSQL

```sql:update_seq.ddl
alter table TKNPATD                   alter column N_SEQ                restart with 1          ;
alter table TEVNTLOG                  alter column SEQ_NO               restart with 63372804            ;
alter table TC83TRGWT                 alter column TRG_ID               restart with 103501     ;
```

最大値に1を加算した値で更新します。現行の最大値は以下のSQLで確認します。
db2 select max(N_SEQ) from TKNPATD

コマンドでシステムテーブルからカウンターを更新するSQLを生成します。
db2 -txf update_seq.gen -z update_seq.sql
db2 -txf update_seq.sql -z update_seq.ddl
<details><summary> カウンターを更新するSQLを生成するSQL（左の三角をクリックすると展開）</summary><div>

```sql:update_seq.gen
select  'select ''alter table '
    ||  substr(TBNAME,1,25)
    ||  ' alter column '  
    ||  substr(NAME,1,20) 
    ||  ' restart with '' || char(case when max(' 
    ||  rtrim(NAME) 
    ||  ')      is null then 1 else max(' 
    ||  rtrim(NAME) 
    ||  ')+1     end) || '';'' from ' 
    ||  rtrim(TBNAME) 
    ||  ' ;'  
  from SYSIBM.SYSCOLUMNS 
    where GENERATED in('A','D') ;
```
</div></details>

# さいごに
Export/Loadは以下の処理がキモになります。

- 32KBを超えるレコード
- ラージオブジェクト(LOB)
- 自動生成列(GENERATED ALWAYS AS IDENTITY)

最後までお読みいただき誠にありがとうございました。