空間站

          北極心空

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks

          簡介

          當特定事件在 IBM® DB2® Universal Database™ 數(shù)據(jù)庫中發(fā)生時,您就可以激活 觸發(fā)器來執(zhí)行其他一些操作。在本文中,您將在觸發(fā)器的世界里遨游,看看如何通過觸發(fā)器來增強數(shù)據(jù)庫中的業(yè)務(wù)規(guī)則。您還將學(xué)習(xí)如何使用 DB2 UDB Version 8.1 的控制中心來幫助您創(chuàng)建一個應(yīng)用于簡單業(yè)務(wù)場景的簡單觸發(fā)器。

          什么是觸發(fā)器

          當一個指定的 SQL 操作(如 DELETE,INSERT,或者是 UPDATE 操作)作用于某張表時,一個定義了一組操作的觸發(fā)器就可以被激活。觸發(fā)器并不像參照完整性約束和檢查約束那樣,我們甚至可以使用對其他表來進行更新。

          業(yè)務(wù)場景

          將一項技術(shù)應(yīng)用于真實世界的一個場景總是有益的。出于教學(xué)的目的,讓我們在一個銀行相關(guān)環(huán)境中研究觸發(fā)器,在該模擬環(huán)境中,我們僅僅建立了一張表。再次強調(diào),這是被簡化了的!我們將要做的是,運用觸發(fā)器來促進銀行提供的透支保護。例如,一個銀行客戶有一個支票帳戶(checking account)和一個儲蓄帳戶(saving account)。當從支票帳戶中取款的金額超過了該帳戶的余額時,就會發(fā)生一次自動的轉(zhuǎn)帳(叫做透支保護),即自動從客戶的儲蓄帳戶轉(zhuǎn)帳過來。當然,這必須符合一定的條件,即儲蓄帳戶中必須有足夠多的錢來補償透支的金額。

          開始

          像上面所提及的,我們的銀行僅僅包含一張表。在這張表中,我們將存入客戶的支票帳戶和儲蓄帳戶的余額等信息。每個客戶通過其社會保險號碼來標識。下面是對該表的描述:

          表 1. 對 ACCTTABLE 的描述

          Column Name Column Type Nullable?
          SSN* Varchar(11) NO
          LastName Varchar(30) NO
          FirstName Varchar(30) NO
          SavingBalance Decimal (Precision: 7, Scale: 2) NO
          CheckingBalance Decimal (Precision: 7, Scale: 2) NO
          * 表示主鍵

          請使用 DB2 命令行處理器為上面的表創(chuàng)建一個數(shù)據(jù)庫。將數(shù)據(jù)庫命名為 bnkdb。

          db2 => create database bnkdb

          接下來,連接到該數(shù)據(jù)庫。我假設(shè)您已經(jīng)在您的機器上有了一個用戶名為 db2admin 密碼為 db2admin 的帳號。

          db2 => connect to bnkdb user db2admin using db2admin

          現(xiàn)在,創(chuàng)建 accttable 表:

          db2 => create table accttable(ssn varchar(30) not null primary key,
                      lastname varchar(30) not null, firstname varchar(30) not null,
                      savingbalance decimal(7,2) not null, checkingbalance decimal(7,2) not null)
                      

          現(xiàn)在向所創(chuàng)建的表中加入兩條記錄:

          db2 => insert into accttable values
                      ('111-11-1111','Bhogal','Kulvir',1500.00,1000)
                      
          db2 => insert into accttable values
                      ('222-22-2222','Guy','Someother',2000.00,4000)
                      

          觸發(fā)器可以在對表的一次 INSERT、 DELETE 或者 UPDATE 操作 之前之后啟動。在我們的例子中,您將創(chuàng)建一個在對ACCTTABLE 表執(zhí)行 UPDATE 操作之前啟動的觸發(fā)器。在觸發(fā)器術(shù)語中,INSERT、 DELETE 或者 UPDATE 這些使得觸發(fā)器啟動的事件被稱作 觸發(fā)事件。觸發(fā)器的啟動是在觸發(fā)事件之前還是之后則稱為觸發(fā)器的 激活時間

          使用 Control Center 創(chuàng)建觸發(fā)器

          打開 DB2 Control Center 開始創(chuàng)建觸發(fā)器,展開您創(chuàng)建的數(shù)據(jù)庫(即 bnkdb),鼠標右鍵點擊 Triggers 選項并且選擇 Create.....

          圖 1. 創(chuàng)建觸發(fā)器

          Figure 1.

          Create Trigger 屏幕中,可以指定觸發(fā)器所在的模式。請選擇 DB2ADMIN 模式。記住,觸發(fā)器是與表相關(guān)的,所以我們需要選擇相關(guān)表的模式。然后請再次選擇 DB2ADMIN 模式:

          圖 2. 選擇模式

          Figure 2.

          在同一個屏幕中, 需要指定一個觸發(fā)器的名字。將觸發(fā)器命名為 OVERDRAFT。而且,需要指定與該觸發(fā)器相關(guān)的表的名字。這里選擇您創(chuàng)建的 ACCTTABLE

          圖 3. 選擇您創(chuàng)建的表

          Figure 3.

          Time to trigger action區(qū)域中,選擇 Before

          圖 4. 選擇 Before

          Figure 4.

          Operation that causes the trigger to be executed區(qū)域中選擇 Update of columns 操作并且指定被操作列為 CHECKINGBALANCE

          圖 5.

          Figure 5. Specify the column name

          點擊 Triggered action標簽頁來創(chuàng)建該觸發(fā)器:

          圖 6. 構(gòu)建觸發(fā)器

          Figure 6.

          指定臨時變量

          DB2 UDB 能夠跟蹤在啟動觸發(fā)器的那條語句之前和之后的一行的狀態(tài)。請在 Correlation name for the old rows 一欄中填入 OLDROW,Correlation name for the new rows 一欄中填入 NEWROW

          圖 7. 指定 NEWROW

          Figure 7.

          注意,您也許無法指定其中的一個 correlation name,因為它依賴于引起觸發(fā)器啟動的特定操作和激活時間的組合。例如,假設(shè)您的觸發(fā)器選擇的 Time to trigger actionBefore,觸發(fā)事件是 DELETE 語句。在這種情況下,我們就無法指定一個 "Correlation name for the new rows"。為什么呢?因為在執(zhí)行了一個刪除操作以后,新行是不存在的。

          因為您創(chuàng)建的觸發(fā)器是在 UPDATE 之前被激活,所以不能編輯 Temporary table for the old rowsTemporary table for the new rows選項。

          您將注意到,在這種情況下(一個在 UPDATE 之前被激活的觸發(fā)器),您只能指定觸發(fā)器針對 而不是針對每個 語句觸發(fā)。

          圖 8. 觸發(fā)器觸發(fā)于每行

          Figure 8.

          引起觸發(fā)事件的語句可能會同時影響數(shù)據(jù)庫中的多行。"For each Row" 選項意味著觸發(fā)器將在每一行被修改時激活。另一方面,"For each statement" 選項("before" 型觸發(fā)器是不允許的)則意味著觸發(fā)器定義的操作只在調(diào)用一次 SQL 語句后執(zhí)行一次。

          可以點擊 Show SQL按鈕來看看底層的 SQL 語句到目前為止是什么樣子:

          圖 9. Show SQL 框

          Figure 9.

          構(gòu)建觸發(fā)動作

          現(xiàn)在該創(chuàng)建觸發(fā)動作了。我們的業(yè)務(wù)規(guī)則是支票帳戶的余額必須低于 0 才能激活該觸發(fā)器。也就是說,我們需要指定 search-condition 為 NEWROW.CHECKINGBALANCE<0 。我們之所以指定 NEWROW.CHECKINGBALANCE 是因為需要分析在 update 操作之后支票帳戶的余額將會是多少。

          創(chuàng)建觸發(fā)器主體

          現(xiàn)在我們將要在 Triggered Action 文本區(qū)域中替換 triggered-SQL-statement (參見下面)。

          圖 10. 創(chuàng)建觸發(fā)器語句

          Figure 10.

          用來替換的代碼如下:

          declare overage decimal (7,2);
                      set overage = (NEWROW.CHECKINGBALANCE*-1);
                      if overage>OLDROW.SAVINGBALANCE then SIGNAL SQLSTATE '70001'
                      ('Overdraft Protection Unsuccessful');
                      else set newrow.savingbalance =
                      oldrow.savingbalance-overage, newrow.checkingbalance = 0;
                      end if;
                      

          讓我們一句一句地仔細研究一下這段代碼。在觸發(fā)器主體中,可以聲明將要在主體中使用的變量。我們使用下面這行代碼來聲明變量: decimal(7,2)declare overage decimal (7,2) ; 這樣就定義了一個類型為 decimal(7,2) 名為 overage 的變量。

          下一步我們將 overage 變量的值設(shè)置為 (NEWROW.CHECKINGBALANCE*-1) ;

          我們將使用該算式計算出我們想要從支票帳戶取出的超額(overage)的數(shù)目。指定 NEWROW.CHECKINGBALANCE 是因為我們需要分析支票帳戶的余額在 update 操作發(fā)生后是多少。

          set overage = (NEWROW.CHECKINGBALANCE*-1);

          發(fā)出錯誤條件信號

          如果違反了您在觸發(fā)器中定義的業(yè)務(wù)規(guī)則,就可以使用 SIGNAL 語句來拋出一個錯誤條件信號。在我們的例子中,不允許有人擁有的支票帳戶余額為負數(shù)。如果有人想要將支票帳戶的余額列更新為一個負數(shù),我們就可以試著看看在儲蓄帳戶中是否有足夠多的錢來補償這個負數(shù)。如果沒有,那么就可以發(fā)出一條 SQL 狀態(tài)為 '70001' 的信息 "'Overdraft Protection Unsuccessful"。

          認識到包含 SIGNAL 語句的效果是很重要的。SIGNAL 語句回滾由觸發(fā)語句(也就是我們的 update 語句)嘗試的更改。SIGNAL 語句也將回滾在觸發(fā)器內(nèi)發(fā)生的更改。此外,假設(shè)我們是使用 Java™ 應(yīng)用程序來與數(shù)據(jù)庫進行交互的,并且試圖執(zhí)行一次會觸發(fā)我們的觸發(fā)器并違反業(yè)務(wù)規(guī)則的 update 操作。Java 應(yīng)用程序?qū)⒔邮芪覀兯付ǖ?SQLSTATE 以及值為 -438 的 SQLCODE。在這行中我們使用 SIGNAL SQLSTATE 屬性:

          if overage>OLDROW.SAVINGBALANCE then SIGNAL SQLSTATE '70001'
                      ('Overdraft Protection Unsuccessful');
                      

          該行說明,如果我們的 overage 比儲蓄帳戶的余額數(shù)目還要大,那么就需要拋出一個危險信號。

          轉(zhuǎn)帳

          如果儲蓄帳戶的余額數(shù)目足夠補償超出的數(shù)目,這時就會發(fā)生轉(zhuǎn)帳。如果滿足這種條件,我們將對新行作兩處修改:

          1. 修改 "new row" 的 savingbalance 列,將其減去 overage 以促成透支轉(zhuǎn)帳。
          2. 將支票帳戶的余額設(shè)置為 0。我們使用下面的代碼來完成:

          
                      else set newrow.savingbalance = oldrow.savingbalance-overage,
                      newrow.checkingbalance = 0; end if;
                      

          最終結(jié)果

          可以再次通過 Show SQL按鈕來看看最后的結(jié)果:

          圖 11.顯示 SQL

          Figure 11.

          在點擊 Close之后,您將看到 OVERDRAFT 觸發(fā)器已經(jīng)創(chuàng)建好了:

          圖 12. 創(chuàng)建 OVERDRAFT 觸發(fā)器

          Figure 12.

          測試

          可以通過一個 update 語句來進行測試。打開命令行編寫下面的語句:

          db2=> update accttable set checkingbalance = -500 where ssn='111-11-1111'
                      

          根據(jù)我們創(chuàng)建的業(yè)務(wù)邏輯,這個 update 操作將啟動該觸發(fā)器,由于支票帳戶透支,該觸發(fā)器將從 savingbalance 列取出 500.00 到支票帳戶。因此,SSN 為 111-11-1111 的帳戶的 checkingbalance 會變成 0.00 而 savingbalance 將變成 1000.00(原來的余額 1500 - 透支的 500)。下面的查詢驗證了該結(jié)果:

          圖 13. 查詢

          Figure 13.

          尾聲

          您已經(jīng)在一個假想的業(yè)務(wù)場景中創(chuàng)建了一個 DB2 觸發(fā)器。觸發(fā)器是 DB2 數(shù)據(jù)庫的一個非常強大的特性,您可以使用它將業(yè)務(wù)邏輯分化到關(guān)系數(shù)據(jù)庫這邊。如果考慮到有多個應(yīng)用程序都將與同一個數(shù)據(jù)庫進行交互,您就會發(fā)現(xiàn)這種分化是非常強大的。在一個大型企業(yè)中,您可能多次遇到過這樣的情況,即不知道要創(chuàng)建的是怎樣一個將與數(shù)據(jù)庫交互的應(yīng)用程序。與其只是希望這些應(yīng)用程序遵守被認為是您的組織機構(gòu)的戒律的業(yè)務(wù)規(guī)則,還不如使用觸發(fā)器作為您工具箱中的一種工具,以確保現(xiàn)在和將來與數(shù)據(jù)庫進行交互的所有的應(yīng)用程序強制遵守這些業(yè)務(wù)規(guī)則。一個觸發(fā)器只能關(guān)聯(lián)一個表,而不能關(guān)聯(lián)一個視圖。您也許可以考慮使用 INSTEAD OF 觸發(fā)器來與視圖交互。


          posted on 2007-07-24 15:47 蘆葦 閱讀(1469) 評論(2)  編輯  收藏 所屬分類: 數(shù)據(jù)庫IBM

          Feedback

          # re: 在DB2中創(chuàng)建第一個觸發(fā)器 2007-07-24 15:52 蘆葦
          我自己曾用到的:
          --建觸發(fā)器
          --
          修改登錄人信息觸發(fā)
          create trigger db2admin.t_userlogin_upd after update on db2admin.USER_LOGIN referencing new as n  old AS o for each row
          begin atomic
            
          declare TEMP_IBMSNAP_COMMITSEQ bigint;
            
          IF ((n.WORK_ID <> o.WORK_ID) or (n.USER_LOGIN_ID <> o.USER_LOGIN_ID) or (n.CURRENT_PASSWORD <> o.CURRENT_PASSWORD)) THEN      
              
          set TEMP_IBMSNAP_COMMITSEQ = nextval for db2admin.seq_name;
              
          INSERT INTO db2admin.CCD_USER_LDAP VALUES (TEMP_IBMSNAP_COMMITSEQ, 0'U', n.PARTY_ID, n.USER_LOGIN_ID , n.CURRENT_PASSWORD , 'CLERK_NAME' , n.WORK_ID , '1' );
                
          UPDATE db2admin.CCD_USER_LDAP SET CLERK_NAME = (select CLERK_NAME from CLERK where PARTY_ID =n.PARTY_ID) where IBMSNAP_COMMITSEQ = TEMP_IBMSNAP_COMMITSEQ;
            
          end if;
          end


          --新增登錄人信息觸發(fā)
          create trigger db2admin.t_userlogin_ins after insert on db2admin.USER_LOGIN referencing new as n for each row 
          begin atomic
              
          declare TEMP_IBMSNAP_COMMITSEQ bigint;
              
          set TEMP_IBMSNAP_COMMITSEQ = nextval for db2admin.seq_name;
             
          INSERT INTO db2admin.CCD_USER_LDAP VALUES (TEMP_IBMSNAP_COMMITSEQ, 0'I', n.PARTY_ID, n.USER_LOGIN_ID , n.CURRENT_PASSWORD , 'CLERK_NAME' , null , '1');
             
          UPDATE db2admin.CCD_USER_LDAP SET CLERK_NAME = (select CLERK_NAME from CLERK where PARTY_ID =n.PARTY_ID) where IBMSNAP_COMMITSEQ = TEMP_IBMSNAP_COMMITSEQ;
          end

          --修改登錄人姓名或狀態(tài)觸發(fā)
          create trigger db2admin.t_userinfo_upd after update on db2admin.CLERK referencing new as n  old AS o for each row
          begin atomic
            
          declare TEMP_IBMSNAP_COMMITSEQ bigint;
            
          IF ((n.CLERK_NAME <> o.CLERK_NAME) or (n.CLERK_STATUS <> o.CLERK_STATUS)) THEN         
              
          set TEMP_IBMSNAP_COMMITSEQ = nextval for db2admin.seq_name;
              
          INSERT INTO db2admin.CCD_USER_LDAP VALUES (TEMP_IBMSNAP_COMMITSEQ, 0'U', n.PARTY_ID, 'USER_LOGIN_ID' , 'CURRENT_PASSWORD'  , n.CLERK_NAME , 'WORK_ID''0' );
                
          UPDATE db2admin.CCD_USER_LDAP SET USER_LOGIN_ID = (select USER_LOGIN_ID from USER_LOGIN where PARTY_ID =n.PARTY_ID),CURRENT_PASSWORD=(select CURRENT_PASSWORD from USER_LOGIN where PARTY_ID =n.PARTY_ID),WORK_ID=(select WORK_ID from USER_LOGIN where PARTY_ID =n.PARTY_ID) where IBMSNAP_COMMITSEQ = TEMP_IBMSNAP_COMMITSEQ;
            
          end if;
          end

          --刪除觸發(fā)器
          DROP TRIGGER DB2ADMIN.T_USERLOGIN_INS;
          DROP TRIGGER db2admin.T_USERLOGIN_UPD;
          DROP TRIGGER db2admin.T_USERINFO_UPD;
          --創(chuàng)建一個sequence 
          create sequence seq_name start with 1 increment by 1 
          --刪除sequence
          drop sequence seq_name
            回復(fù)  更多評論
            

          # re: 在DB2中創(chuàng)建第一個觸發(fā)器 2007-07-24 15:59 蘆葦
          sequence是一種方法。也可以用用indentity列
          CREATE TABLE table (col1 INT,
          col2 DOUBLE,
          col3 INT NOT NULL GENERATED ALWAYS AS IDENTITY
          (START WITH 100, INCREMENT BY 5))   回復(fù)  更多評論
            

          主站蜘蛛池模板: 林州市| 肃北| 酒泉市| 于都县| 闸北区| 宁陕县| 武隆县| 黔南| 大庆市| 桑植县| 白水县| 巢湖市| 中方县| 剑川县| 从江县| 恭城| 特克斯县| 南汇区| 平泉县| 阿勒泰市| 泽州县| 二连浩特市| 化隆| 蒲城县| 获嘉县| 延寿县| 平阳县| 石景山区| 广南县| 绥棱县| 称多县| 平度市| 靖远县| 徐闻县| 安平县| 临清市| 蒲城县| 游戏| 太和县| 荆州市| 岑溪市|