2009年9月30日 星期三

Avoid from SQL20165N using DB2 9.7 Express-C


/*
想對刪除的資料同時做搬移,但是以下語法一直有錯

INSERT INTO TEST2
SELECT * FROM OLD TABLE
(DELETE FROM TEST WHERE COL1 = 'twn-2');

Server Msg: -20165, State: 428FL, [IBM][CLI Driver][DB2/NT]
SQL20165N 不容許 FROM 子句內的 SQL 資料變更陳述式位在指定它的環境定義中。
SQLSTATE=428FL

但是山不轉,路轉。如果我用 EXPORT/LOAD 呢?
*/

/* 建立測試 Tables & Data */

Create Table TEST
(
  COL1 CHAR(5),
  COL2 CHAR(2),
  COL3 CHAR(12)
) IN USERSPACE1

INSERT INTO TEST VALUES ('kor-1','dv','200706231040');
INSERT INTO TEST VALUES ('kor-1','dv','200706231045');
INSERT INTO TEST VALUES ('kor-1','dv','200706231050');
INSERT INTO TEST VALUES ('kor-1','cu','200706231055');
INSERT INTO TEST VALUES ('kor-1','cu','200706231055');
INSERT INTO TEST VALUES ('kor-1','rv','200706220450');
INSERT INTO TEST VALUES ('kor-1','rv','200706220450');
INSERT INTO TEST VALUES ('kor-1','rv','200706220450');
INSERT INTO TEST VALUES ('kor-1','rv','200706220453');
INSERT INTO TEST VALUES ('twn-2','dv','200706220454');

-- TEST2 存放從 TEST 刪掉的資料
CREATE TABLE TEST2 LIKE TEST IN USERSPACE1;


/* DML都失敗,改測 EXPORT/LOAD */


C:\>db2 declare cur cursor for select * from old table (delete from test where c
ol1 = 'twn-2')
DB20000I SQL 指令已順利完成。

C:\>db2 load from cur of cursor insert into test2
SQL3501W 由於禁止資料庫向前回復, 所以表格常駐的表格空間將不放入備份懸置狀態。

SQL1193I 公用程式正在開始從 SQL 陳述式 " select * from old table (delete from
test where col1 = 'twn-2')" 載入資料。

SQL3500W 公用程式在 "2009-09-30 15:52:19.926580" 時開始 "LOAD" 階段。

SQL3519W 開始載入「一致點」。輸入記錄數 = "0"。

SQL3520W 成功載入「一致點」。

SQL3110N 公用程式已完成處理。自輸入檔讀取第 "1" 列。

SQL3519W 開始載入「一致點」。輸入記錄數 = "1"。

SQL3520W 成功載入「一致點」。

SQL3515W 公用程式已在 "2009-09-30 15:52:20.295444" 時完成 "LOAD" 階段。


已讀取的列數        = 1
已略過的列數        = 0
已載入的列數        = 1
已拒絕的列數        = 0
已拒絕的列數        = 0
已確定的列數        = 1

/* 看一下資料是否 LOAD 到 TEST2 去 */

C:\>db2 select * from test2

COL1 COL2 COL3
----- ---- ------------
twn-2 dv 200706220454

  已選取 1 個記錄。

2009年9月21日 星期一

Autonomous transactions - DB2 9.7 Express-C


嗯?...這個在 Oracle 不是很早就有的嗎?
既然你出了,就意思意思玩一下

註:DB2 9.7還加了幾個 FUNCTIONs 像LAST_DAY,NEXT_DAY等,
  另外,終於可以用 CREATE OR REPLACE語法、
  也可以執行 Oracle PL/SQL。但Express-C版本沒辦法測...


/* 建立測試 table */

CREATE TABLE TEST
(
  COL1 INTEGER,
  COL2 VARCHAR(32)
) IN USERSPACE1;

CREATE TABLE AUTONOMOUS_TAB
(
  SYSDT TIMESTAMP,
  STEP_DESC VARCHAR(32)
) IN USERSPACE1;



/*
 建立測試 procedure sp_inslog 記錄 log
 採Autonomous方式
 在LANGUAGE SQL下方寫上 AUTONOMOUS,告訴 DB2 這支程式用自己的 Transaction
*/


create or replace procedure sp_inslog(in insz varchar(64))
specific sp_inslog
language sql
autonomous
begin
   insert into autonomous_tab
   values (current timestamp,insz);
   commit;
end


/*
 建立測試 procedure sp_proc1
 為一般procedure,裡面呼叫 sp_inslog執行寫log工作
*/


create or replace procedure sp_proc1
specific sp_proc1
language sql
begin
  DECLARE SQLCODE INTEGER DEFAULT 0;
  DECLARE retcode INTEGER DEFAULT 0;

  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION,
                   SQLWARNING,
                   NOT FOUND
  SET retcode = SQLCODE;

  insert into test values (0,'ETL開始日期'||char(current date));
  if (retcode <> 0) and (retcode <> 100) then
    rollback;
    call sp_inslog(char(retcode) || ' 流程0執行失敗');
  else
    call sp_inslog('流程0執行成功');
  end if;

  insert into test values (1,'ETL開始時間'||char(current date));

  IF (retcode <> 0) AND (retcode <> 100) THEN
    rollback;
    call sp_inslog(char(retcode) || ' 流程1執行失敗');
  else
    call sp_inslog('流程1執行成功');
  end if;
  commit;
end

/* 測試成功的狀況 */
C:\>db2 call sp_proc1

 傳回狀態 = 0

/* 看結果 */

C:\>db2 select * from test

COL1     COL2
----------- --------------------------------
      0 ETL開始日期2009-09-21
      1 ETL開始時間2009-09-21

  已選取 2 個記錄。

C:\>db2 select * from autonomous_tab

SYSDT             STEP_DESC
-------------------------- -------------
2009-09-21-17.02.38.141000 流程0執行成功
2009-09-21-17.02.38.201000 流程1執行成功

  已選取 2 個記錄。


/*
 直接改一下 procedure sp_proc1 讓它在執行新增第二次test時失敗
*/


create or replace procedure sp_proc1
specific sp_proc1
language sql
begin
   DECLARE SQLCODE INTEGER DEFAULT 0;
   DECLARE retcode INTEGER DEFAULT 0;

   DECLARE CONTINUE HANDLER FOR SQLEXCEPTION,
                    SQLWARNING,
                    NOT FOUND
   SET retcode = SQLCODE;

   insert into test values (0,'ETL開始日期'||char(current date));
   if (retcode <> 0) and (retcode <> 100) then
      rollback;
      call sp_inslog(char(retcode) || ' 流程0執行失敗');
   else
      call sp_inslog('流程0執行成功');
   end if;

   insert into test values (1,'倉儲系統ETL開始時間'||
                  char(current timestamp));

   IF (retcode <> 0) AND (retcode <> 100) THEN
      rollback;
      call sp_inslog(char(retcode) || ' 流程1執行失敗');
   else
      call sp_inslog('流程1執行成功');
   end if;
   commit;
end

/* 測試失敗的狀況 */
C:\>db2 call sp_proc1

 傳回狀態 = 0


C:\>db2 select * from test

COL1     COL2
----------- --------------------------------

  已選取 0 個記錄。


/* autonomous_tab第一筆資料未隨著外面那層 procedure的rollback而跟著rollback */


C:\>db2 select * from autonomous_tab

SYSDT             STEP_DESC
-------------------------- --------------------------
2009-09-21-17.39.45.593000 流程0執行成功
2009-09-21-17.39.45.614000 -433 流程1執行失敗

已選取 2 個記錄。

2009年9月2日 星期三

Performance of Merge statement using DB2 Express-C 9.7


/*
Update 的語法在 Performance tuning 上被我奉為萬靈丹的 Merge
原來也有不能勝出的時候。

My Scenario:
Table t: col1 以日期帶序號為單號。 Ex:20090903001
Table t1: col1 以單號帶項次。   Ex:2009090300101
                    2009090300102
                    2009090300103
                    2009090300104
                    2009090300105
join 條件: t.col1 = substr(t1.col1,1,11),將 t 的其它欄位值更新至 t1
Merge 語法的效能就差到不行,反而是 Update 的效能超快
*/

/* 建立測試 table t 並新增資料 */

create table t
(
  col1 char(11),
  col2 char(8)
) in userspace1;

INSERT INTO T
WITH N (COL0,COL1,COL2) AS (
SELECT 1 COL0,SUBSTR(CHAR(INT(DATE(SYSDATE))),1,8) ||
        SUBSTR(CHAR_OLD(DECIMAL(1,3,0)),1,3),
        CHAR(INT(DATE(SYSDATE)))
FROM SYSIBM.SYSDUMMY1
UNION ALL
SELECT N.COL0 + 1,SUBSTR(CHAR(INT(DATE(SYSDATE))),1,8) ||
          SUBSTR(CHAR_OLD(DECIMAL(N.COL0 + 1,3,0)),1,3),
          N.COL2
FROM N
WHERE N.COL0 + 1 <= 100)
SELECT COL1,COL2 FROM N;


/* 建立測試 table t1 並新增資料 */

create table t1
(
  col1 char(13),
  col2 char(8)
) in userspace1;

INSERT INTO T1(COL1)
WITH N (COL0,COL1,OCOL1) AS (
SELECT 1 COL0,COL1 || SUBSTR(CHAR_OLD(DECIMAL(1,2,0)),1,2),
     COL1 OCOL1
FROM  T
WHERE MOD(INT(RIGHT(COL1,2)),2) = 0
UNION ALL
SELECT N.COL0 + 1,RTRIM(N.OCOL1) ||
          SUBSTR(CHAR_OLD(DECIMAL(N.COL0 + 1,2,0)),1,2),
          N.OCOL1
FROM N
WHERE N.COL0 + 1 <= 5)
SELECT COL1 FROM N ORDER BY COL1;

/* 將 merge 語法存入 merge.sql */

merge into t1 using t on t.col1 = substr(t1.col1,1,11)
when matched then
update set t1.col2 = t.col2

/* 將 update 語法存入 update.sql */

update t1 set col2 = (select t.col2
             from t where t.col1 = substr(t1.col1,1,11))

/* 執行 db2expln 看 merge 的表現 */
C:\>db2expln -database ORION -g -stmtfile merge.sql -o merge.log

/* 開啟 merge.log */
C:\>notepad merge.log


Statement:
 merge into t1 using t on t.col1 =substr(t1.col1, 1, 11)
 when matched
 then
 update set t1.col2 =t.col2

Section Code Page = 1208
Estimated Cost = 3692.811523
Estimated Cardinality = 240.000000

Access Table Name = ADMINISTRATOR.T ID = 2,9479
| #Columns = 2
| Skip Inserted Rows
| Avoid Locking Committed Data
| Currently Committed for Cursor Stability
| May participate in Scan Sharing structures
| Scan may start anywhere and wrap, for completion
| Scan can be throttled in scan sharing management
| Relation Scan
| | Prefetch: Eligible
| Lock Intents
| | Table: Intent Share
| | Row : Next Key Share
Left Outer Nested Loop Join
| Access Table Name = ADMINISTRATOR.T1 ID = 2,9480
| | #Columns = 0
| | Skip Inserted Rows
| | Evaluate Block/Data Predicates Before Locking Committed Row
| | May participate in Scan Sharing structures
| | Fast scan, for purposes of scan sharing management
| | Relation Scan
| | | Prefetch: Eligible
| | Isolation Level: Read Stability
| | Lock Intents
| | | Table: Intent Exclusive
| | | Row : Update
| | Sargable Predicate(s)
| | | #Predicates = 1

Insert Into Sorted Temp Table ID = t1
| #Columns = 3
| #Sort Key Columns = 1
| | Key 1: (Ascending)
| Sortheap Allocation Parameters:
| | #Rows   = 250.000000
| | Row Width = 28
| Piped
Access Temp Table ID = t1
| #Columns = 3
| Relation Scan
| | Prefetch: Eligible
Residual Predicate(s)
| #Predicates = 4
Residual Predicate(s)
| #Predicates = 1
Establish Row Position
| Access Table Name = ADMINISTRATOR.T1 ID = 2,9480
Update: Table Name = ADMINISTRATOR.T1 ID = 2,9480
| Update Predicate(s)
| | #Predicates = 1
End of section

Optimizer Plan:
                  Rows
                 Operator
                  (ID)
                  Cost
                  240
                 UPDATE
                  ( 2)
                 3692.81
               /--/     \
            240         250
           FETCH        Table:
           ( 3)        ADMINISTRATOR
          1877.31        T1
          /   \
        240    250
       FILTER   Table:
       ( 4)    ADMINISTRATOR
      61.6852    T1
        |
       250
      FILTER
      ( 5)
      61.6458
        |
       250
      TBSCAN
      ( 6)
      61.2642
        |
       250
      SORT
      ( 7)
      61.2416
        |
       250
      NLJOIN
      ( 8)
      61.1053
     /    \---\
   100       *
   TBSCAN      |
   ( 9)      250
   15.2041    Table:
    |       ADMINISTRATOR
    100      T1
 Table:
 ADMINISTRATOR
 T

/* 執行 db2expln 看 update 的表現 */
C:\>db2expln -database ORION -g -stmtfile update.sql -o update.log

/* 開啟 update.log */
C:\>notepad update.log


Statement:
 update t1 set col2 =
   (select t.col2
   from t
   where t.col1 =substr(t1.col1, 1, 11))

Section Code Page = 1208
Estimated Cost = 1952.518555
Estimated Cardinality = 250.000000

Access Table Name = ADMINISTRATOR.T1 ID = 2,9480
| #Columns = 1
| Skip Inserted Rows
| May participate in Scan Sharing structures
| Relation Scan
| | Prefetch: Eligible
| Lock Intents
| | Table: Intent Exclusive
| | Row : Exclusive

Nested Loop Join
| Piped Inner
| Access Table Name = ADMINISTRATOR.T ID = 2,9479
| | #Columns = 1
| | Skip Inserted Rows
| | Avoid Locking Committed Data
| | Currently Committed for Cursor Stability
| | May participate in Scan Sharing structures
| | Scan may start anywhere and wrap, for completion
| | Fast scan, for purposes of scan sharing management
| | Scan can be throttled in scan sharing management
| | Relation Scan
| | | Prefetch: Eligible
| | Lock Intents
| | | Table: Intent Share
| | | Row : Next Key Share
| | Sargable Predicate(s)
| | | #Predicates = 1
Update: Table Name = ADMINISTRATOR.T1 ID = 2,9480
End of section

Optimizer Plan:
              Rows
             Operator
              (ID)
              Cost
              250
             UPDATE
              ( 2)
             1952.52
           /--/    \
        250        250
       NLJOIN      Table:
        ( 3)      ADMINISTRATOR
       61.3672     T1
      /    \
    250      1
   TBSCAN     TBSCAN
   ( 4)      ( 5)
   22.8603    15.2357
    |        |
   250       100
 Table:       Table:
 ADMINISTRATOR   ADMINISTRATOR
 T1         T



Merge 的 Estimated Cost = 3692.811523
Update 的 Estimated Cost = 1952.518555

Limitation of Using Merge in DB2 using DB2 Express-C 9.7


Merge Update 有一定的限制,例如想更新的 Table 是個 Nickname 就不行
My Scenario:
Database1: Taurus 建立 Table b1
Database2: Orion 建立 Table a1
         建立 Nickname b1 federated from Taurus

在 Database Orion 對 b1 以 Merge 方式將 a1 的值更新到 b1,則會產生錯誤
SQL0270N 不支援函數 (原因碼 = "67")。 SQLSTATE=42997

67  請勿在 MERGE 陳述式中,指定暱稱或暱稱的視圖作為目標。


My Solutions:
1. 能以傳統Update的語法做更新
2. 在 Database Taurus 上建立 nickname a1,在 Taurus 對 b1 做 merge 語法更新之。

※ 無法藉由 Wrapper Server 增加參數 DB2_TWO_PHASE_COMMIT = 'Y' 來達到目的。


/* 建立測試環境 */

C:\>db2 connect to orion

資料庫連線資訊

資料庫伺服器 = DB2/NT 9.7.0
SQL 授權 ID = ADMINIST...
本端資料庫別名 = ORION

C:\>db2 create table a1 ( col1 integer,col2 varchar(16)) in userspace1
DB20000I SQL 指令已順利完成。

C:\>db2 insert into a1 values (1,'TESTING'),(2,'PRODUCTION')
DB20000I SQL 指令已順利完成。

C:\>db2 terminate
DB20000I TERMINATE 指令已順利完成。

C:\>db2 connect to taurus

  資料庫連線資訊

 資料庫伺服器      = DB2/NT 9.7.0
 SQL 授權 ID      = ADMINIST...
 本端資料庫別名     = TAURUS

C:\>db2 create table b1 ( col1 integer,col2 varchar(16)) in userspace1
DB20000I SQL 指令已順利完成。

C:\>db2 insert into b1(col1) values (1),(2)
DB20000I SQL 指令已順利完成。

C:\>db2 terminate
DB20000I TERMINATE 指令已順利完成。


/* 建立對應Nickname */

C:\>db2 connect to orion

  資料庫連線資訊

 資料庫伺服器      = DB2/NT 9.7.0
 SQL 授權 ID      = ADMINIST...
 本端資料庫別名     = ORION

C:\>db2 create nickname administrator.b1 for w2taurus.administrator.b1
DB20000I SQL 指令已順利完成。

 
/* 在 Orion Database 上對 Nickname 做 Merge Update */

C:\>db2 merge into b1 using a1 on a1.col1 = b1.col1 when matched then update set
b1.col2 = a1.col2

 
/* 產生錯誤訊息 */

DB21034E 指令被當作 SQL 陳述式處理,因為它不是有效的「指令行處理器」指令。 在 SQL 處理程序期間,它已傳回:
SQL0270N 不支援函數 (原因碼 = "67")。 SQLSTATE=42997

/* 不信邪,想試試 Two phase commit */

C:\>db2 connect to orion

  資料庫連線資訊

 資料庫伺服器      = DB2/NT 9.7.0
 SQL 授權 ID      = ADMINIST...
 本端資料庫別名     = ORION

C:\>db2 alter server w2taurus options (add DB2_TWO_PHASE_COMMIT 'Y')
DB20000I SQL 指令已順利完成。

 
/* 依舊失敗 */

C:\>db2 merge into b1 using a1 on a1.col1 = b1.col1 when matched then update set
b1.col2 = a1.col2

DB21034E 指令被當作 SQL 陳述式處理,因為它不是有效的「指令行處理器」指令。 在SQL 處理程序期間,它已傳回:
SQL0270N 不支援函數 (原因碼 = "67")。 SQLSTATE=42997

 
/* 再建在 Taurus 上試試 */

C:\>db2 connect to taurus

  資料庫連線資訊

 資料庫伺服器      = DB2/NT 9.7.0
 SQL 授權 ID      = ADMINIST...
 本端資料庫別名     = TAURUS

C:\>db2 alter server w2orion options(add DB2_TWO_PHASE_COMMIT 'Y')
DB20000I SQL 指令已順利完成。


/* 回到 Database Orion 再試 */

C:\>db2 connect to orion

  資料庫連線資訊

 資料庫伺服器      = DB2/NT 9.7.0
 SQL 授權 ID      = ADMINIST...
 本端資料庫別名     = ORION

C:\>db2 merge into b1 using a1 on a1.col1 = b1.col1 when matched then update set
b1.col2 = a1.col2
DB21034E 指令被當作 SQL 陳述式處理,因為它不是有效的「指令行處理器」指令。 在SQL 處理程序期間,它已傳回:
SQL0270N 不支援函數 (原因碼 = "67")。 SQLSTATE=42997