[Lab 5] PL/SQL オブジェクトの移行

[Lab 5]
PL/SQL オブジェクトの
オブジェクトの移行
2009 年 12 月
日本アイ
日本アイ・
アイ・ビー・
ビー・エム株式会社
エム株式会社
Contents
Contents
CONTENTS............................................................................................................2
CONTENTS
1.
はじめに .........................................................................................................3
1.1
2.
独立した
独立したタイプ
したタイプの
タイプの宣言......................................................................................4
宣言
2.1
2.2
2.3
3.
ハンズオンで使用する DDL について ..........................................................3
スタンドアロン・タイプの移行.......................................................................4
パッケージ Envelope の作成.......................................................................6
タイプを参照する箇所の修正......................................................................6
パッケージの
パッケージの作成............................................................................................8
作成
3.1 パッケージ ACCOUNT_PACKAGE の作成 ...................................................8
3.1.1
AVERAGE_BNAD 関数の作成 ................................................................8
3.1.2
パッケージ仕様部の作成 .....................................................................10
3.1.3
パッケージ本体の作成 .........................................................................11
3.2 パッケージ OFFICE_PACKAGE の作成......................................................14
3.2.1
パッケージ仕様部の作成 .....................................................................16
3.2.2
パッケージ本体の作成 .........................................................................17
4.
プロシージャーの
プロシージャーの作成 ...................................................................................21
4.1
4.2
4.3
4.4
4.5
COUNT_PROJECTS のパラメーター確認...................................................21
COUNT_PROJECTS のプロシージャーへの書き直し ..................................22
COUNT_PROJECTS プロシージャーの作成 ...............................................23
ADD_NEW_EMPLOYEE プロシージャーの作成............................................26
ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT プロシージャーの作成 ...............29
5.
VIEW の作成 .................................................................................................31
6.
トリガーの
トリガーの作成 ..............................................................................................33
6.1
6.2
Update_Departments トリガーの作成 .........................................................33
UPDATE_ACC_ON_NEW_EMPL トリガーの作成...........................................40
2
1.
はじめに
このハンズオンでは、Oracle から抽出した DDL を元に、DB2 への投入を行い、PL/SQL のプロ
シージャーや関数を作成します。このハンズオンで行う作業は下記となります。
パッケージの作成
プロシージャーの作成
関数の作成
ビューの作成
トリガーの作成
このハンズオンで使用するディレクトリーの構成は下記の様になっています。PL/SQL のオブジ
ェクトを作成するための DDL は/workshop/lab5/plsql_ddl を参照してください。
/workshop
└─lab5
:Anonymous block など
├─plsql_ddl
:Lab5 で使う PL/SQL DDL 作業フォルダ
└─plsql_ddl_modified
:変更済み DDL
1.1
ハンズオンで
ハンズオンで使用する
使用する DDL について
このハンズオンで使用する DDL は、Oracle から IBM Data Movement Tool を使用して抽出し
たものです。Data Movement Tool は DDL 抽出の際、オブジェクトの種別ごとファイルを作成し
ます。そのため、一つの DDL ファイルには複数のオブジェクトの DDL が格納されます。例えば、
一つのファイルに複数のオブジェクトが格納されている場合、ハンズオンの手順が煩雑となる
ため、このハンズオン中では下記の様にして、オリジナルの DDL ファイルをオブジェクトごとに
分割しています。
オリジナルの DDL:
db2plsql_package.db2
分割した DDL:
db2plsql_package.db2.account_package
db2plsql_package.db2.envelope
db2plsql_package.db2.office_package
分割された DDL は/workshop/lab5/plsql_ddl ディレクトリに格納されています。
3
2.
独立した
独立したタイプ
したタイプの
タイプの宣言
このセクションでは、スタンドアロン・タイプを取り扱います。「スタンドアロン・タイプ」とは、
PL/SQL オブジェクトとして作成されるタイプの中で、パッケージ中で宣言されるのではなく、独
立して宣言されるものです。
このハンズオンには 1 つのスタンドアロン・タイプが含まれます。IBM Data Movement Tool は
下記の 2 つのファイルにスタンドアロン・タイプを抽出します。
db2plsql_type.db2
db2plsql_type_body.db2
DB2 9.7 はスタンドアロン・タイプをサポートしていないため、別のオブジェクトとして作成する必
要があります。移行先のオブジェクトとして、パッケージが使用可能です。パッケージは、プロシ
ージャーやファンクション、タイプや変数の宣言をまとめて取り扱うための単位となります。パッ
ケージの一要素としてタイプを宣言することで、独立したタイプと同様にタイプの作成が可能に
なります。ただし、そのタイプを使用する際はパッケージの名称をスキーマ(修飾子)として追加
する必要があります。
2.1
スタンドアロン・
スタンドアロン・タイプの
タイプの移行
このステップではスタンドアロン・タイプをパッケージに移行します。移行先のパッケージとして
パッケージ「ENVELOPE」を使用します。下記に示す様に、このパッケージは変数宣言のみを含
んでいます。
移行元となるスタンドアロン・タイプ「EMP_INFO_TYPE」の DDL は db2plsql_type.db2 に含まれて
います。このタイプ宣言を、パッケージ ENVELOPE の中へ移行します。
下記のコマンドを実行して EMP_INFO_TYPE の DDL を確認してください。
cd /workshop/lab5/plsql_ddl
cat db2plsql_type.db2
実行例
db2inst1@db2V97onSLES10:/workshop/lab5> cd /workshop/lab5/plsql_ddl
db2inst1@db2V97onSLES10:/workshop/lab5/plsql_ddl> cat db2plsql_type.db2
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
--#SET :TYPE:SALES:EMP_INFO_TYPE
CREATE OR REPLACE TYPE "SALES"."EMP_INFO_TYPE" AS OBJECT (
EMP_ID NUMBER(5),
FIRST_NAME VARCHAR2(20),
LAST_NAME VARCHAR2(20),
BAND CHAR(1),
HIRE_DATE
TIMESTAMP(3))
/
<省略>
4
_パッケージ ENVELOPE へのタイプ宣言追加
スタンドアロン・タイプをパッケージ中のタイプ宣言に移行する際は、下記の様な変更を行ってく
ださい。
・ 「CREATE OR REPLACE」キーワードの除去
・ 「SALES」スキーマの除去
・ 「AS OBJECT」キーワードを、「IS RECORD」へ変更
・ 宣言の最終部分を「/」から「;(セミコロン)」へ変更
パッケージ ENVELPE の変更は下記の手順で行います
1.vi エディターでパッケージ ENVELOPE の DDL ファイルを開きます
vi db2plsql_package.db2.envelope
2.db2plsql_type.db2 ファイルから、EMP_INFO_TYPE の DDL をコピーします
3.前述の変更を行います
変更例
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
--#SET :PACKAGE:SALES:ENVELOPE
CREATE OR REPLACE PACKAGE "SALES"."ENVELOPE" AS
-/*
-||-----------------------------------------------------------------|| DESCRIPTION: The package to store the declaration of types
-||
-||-----------------------------------------------------------------*/
-- type declaration
TYPE RCT1 IS REF CURSOR;
-- add type declaration for standalone type
TYPE "EMP_INFO_TYPE" IS RECORD (
EMP_ID NUMBER(5),
FIRST_NAME VARCHAR2(20),
LAST_NAME VARCHAR2(20),
BAND CHAR(1),
HIRE_DATE
TIMESTAMP(3));
END ENVELOPE;
/
TERMINATE
/
このハンズオンでは、vi エディターを使用して DDL を編集することを想定していますが、時間の
ない方や vi の操作に不慣れな方は、/workshop/lab5/plsql_ddl_modified/ディレクトリーに修正
済みの DDL がありますので、こちらを使用していただく事も可能です。このハンズオンで今後
登場する DDL は、全て同様に修正済みの DDL が格納されています。
5
2.2
パッケージ Envelope の作成
前ステップで DDL の変更が完了したので、パッケージ ENVELOPE を作成します。
下記のコマンドを実行して、パッケージを作成してください。
db2 -tvf db2plsql_package.db2.envelope
実行例
/workshop/lab5/plsql_ddl_modified> db2 -tvf db2plsql_package.db2.envelope
<省略>
SET CURRENT SCHEMA = 'SALES'
DB20000I SQL コマンドが正常に完了しました。
CREATE OR REPLACE PACKAGE "SALES"."ENVELOPE" AS
-/*
-||------------------------------------------------------------------------|| DESCRIPTION: The package to store the declaration of types
-||
-||------------------------------------------------------------------------*/
-- type declaration
TYPE RCT1 IS REF CURSOR;
-- add type declaration for standalone type
TYPE "EMP_INFO_TYPE" IS RECORD (
EMP_ID NUMBER(5),
FIRST_NAME VARCHAR2(20),
LAST_NAME VARCHAR2(20),
BAND CHAR(1),
HIRE_DATE
TIMESTAMP(3));
END ENVELOPE;
DB20000I
SQL コマンドが正常に完了しました。
TERMINATE
DB20000I TERMINATE コマンドが正常に完了しました。
2.3
タイプを
タイプを参照する
参照する箇所
する箇所の
箇所の修正
EMP_INFO_TYPE を ENVELOPE パッケージ内での宣言に変更したことで、EMP_INFO_TYPE を
参照する際には ENVELOPE を修飾子として追加する必要があります。下記のコマンドを実行
して、移行対象の PL/SQL オブジェクトから、EMP_INFO_TYPE を参照している部分を確認して
ください。
cd /workshop/lab5/plsql_ddl
head -99999 db2plsql* |egrep -i "^==|EMP_INFO_TYPE"
このコマンドは、「db2plsql」をファイル名に持つファイルから、EMP_INFO_TYPE を含む行を抽出
します。
6
コマンド実行例
/> cd /workshop/lab5/plsql_ddl
/workshop/lab5/plsql_ddl> head -99999 db2plsql* |egrep -i "^==|EMP_INFO_TYPE"
==> db2plsql_function.db2.average_band <==
==> db2plsql_function.db2.count_projects <==
==> db2plsql_package.db2.account_package <==
==> db2plsql_package.db2.envelope <==
==> db2plsql_package.db2.office_package <==
==> db2plsql_package_body.db2.account_package <==
==> db2plsql_package_body.db2.office_package <==
==> db2plsql_procedure.db2.add_new_employee <==
o_Employee OUT EMP_INFO_TYPE,
==> db2plsql_procedure.db2.assign_employee_to_new_account <==
==> db2plsql_procedure.db2.get_employee_resume <==
==> db2plsql_trigger.db2.update_acc_on_new_empl <==
==> db2plsql_trigger.db2.update_departments <==
==> db2plsql_type.db2 <==
--#SET :TYPE:SALES:EMP_INFO_TYPE
CREATE OR REPLACE TYPE "SALES"."EMP_INFO_TYPE" AS OBJECT (
==> db2plsql_type_body.db2 <==
==> db2plsql_views.db2.organization_structure <==
この結果から、EMP_INFO_TYPE を参照するオブジェクトは、プロシージャー
「ADD_NEW_EMPLOYEE」のみであることがわかります。
vi エディターを使用して DDL ファイル「db2plsql_procedure.db2.add_new_employee」を開き、
ENVELOPE パッケージ中の EMP_INFO_TYPE への参照が問題なく行えるよう、
ADD_NEW_EMPLOYEE 中の該当行を編集し、ENVELOPE を追加してください。
vi db2plsql_procedure.db2.add_new_employee
編集前
編集後
: o_Employee OUT EMP_INFO_TYPE,
: o_Employee OUT ENVELOPE.EMP_INFO_TYPE,
ENVELOPE.
ここで編集した DDL は、後続のステップで使用します。
7
3.
パッケージの
パッケージの作成
この章では、パッケージの作成を行います。先ほどの演習でも登場しましたが、パッケージとは
プロシージャーや関数、タイプや変数の宣言を一括で取り扱うための単位です。パッケージは、
パッケージ仕様部およびパッケージ本体という、2 つの主要コンポーネントで構成されます。
パッケージ仕様部 はパッケージの外部に対して公開されたパブリック・インターフェースで、当
該パッケージの外部から参照可能な要素で構成されます。パッケージ仕様部を作成するには、
CREATE PACKAGE ステートメントを実行します。
パッケージ本体 には、パッケージ仕様部内で宣言されたプロシージャーおよび関数の、実際の
振る舞いを定義するための実装を記述します。また、それ以外にもパッケージ気外部に公開し
ないプライベートのタイプ、変数、およびカーソルの宣言を行うことができます。パッケージ本体
を作成するには、CREATE PACKAGE BODY ステートメントを実行します。
この章では、下記に示す 2 つのパッケージを作成します。
ACCOUNT_PACKAGE
OFFICE_PACKAGE
3.1
パッケージ ACCOUNT_PACKAGE の作成
パッケージ ACCOUNT_PACKAGE を作成します。このパッケージは、パッケージの前が示す様
に ACCOUNT データを取り扱う処理をひとまとめにして管理しています。
パッケージのメンバーとしては下記の 4 つのプロシージャー
Add_Account
Remove_Account
Account_List
Display_Account_List
及び、タイプ「customer_name_cache」を含みます。
前述したように、どのような要素を含むかはパッケージ本体を見ることでわかります。
ACCOUNT_PACKAGE のパッケージ本体は「db2plsql_package.db2.account_package」に格納さ
れています。
3.1.1
AVERAGE_BNAD 関数の
関数の作成
ACCOUNT_PACKAGE のメンバーの一つ、Display_Account_List プロシージャーでは
AVERAGE_BNAD 関数を参照しています。そのため、AVERAGE_BNAD 関数を先に作成します。
この関数は、部署コードとアカウントコードを引数として受け取り、該当の部署コード・アカウント
コードを持つ従業員を EMPLOYEES 表から検索した上で、職位の平均を戻します。
8
AVERAGE_BNAD 関数の DDL
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
--#SET :FUNCTION:SALES:AVERAGE_BAND
CREATE OR REPLACE FUNCTION "SALES"."AVERAGE_BAND" (p_Department IN
employees.dept_code%TYPE, p_ACCT_ID IN employe
es.ACCT_ID%TYPE)
RETURN CHAR AS
-- variable declaration
v_AverageBAND
CHAR(1);
v_NumericBand
NUMBER;
v_TotalBand
NUMBER:=0;
v_NumberEmployees NUMBER;
-- CURSOR declaration
CURSOR c_Employees IS
SELECT band
FROM employees
WHERE dept_code = p_Department
AND acct_id = p_ACCT_ID;
BEGIN
OPEN c_Employees;
LOOP
FETCH c_Employees INTO v_NumericBand;
EXIT WHEN c_Employees%NOTFOUND;
v_TotalBand := v_TotalBand + v_NumericBand;
END LOOP;
v_NumberEmployees:=c_Employees%ROWCOUNT;
IF(v_NumberEmployees = 0) THEN
RAISE_APPLICATION_ERROR(-20001, 'No employees exist for ' || p_Department || '
' || p_ACCT_ID);
END IF;
SELECT DECODE(ROUND(v_TotalBand/v_NumberEmployees), 5, 'A', 4, 'B', 3, 'C', 2,
'D', 1, 'E')
INTO v_AverageBand FROM dual;
RETURN v_AverageBand;
END Average_Band;
/
この関数は、ソースコードの変更無しにそのまま DB2 で作成可能です。下記のコマンドを使用
して、AVERAGE_BNAD 関数を作成してください。
db2 -tvf db2plsql_function.db2.average_band
9
実行例
$ db2 -tvf db2plsql_function.db2.average_band
<省略>
CREATE OR REPLACE FUNCTION "SALES"."AVERAGE_BAND" (p_Department IN
employees.dept_code%TYPE, p_ACCT_ID IN employe
es.ACCT_ID%TYPE)
<省略>
RETURN v_AverageBand;
END Average_Band;
DB20000I
SQL コマンドが正常に完了しました
この関数が正常に作成できたことを、/workshop/lab5 ディレクトリで下記のスクリプトで確認し
てください。
db2 -tvf call.average_band.clp
このスクリプトは、下記の様な無名ブロックを含みます。この無名ブロックでは部署コード「A00」、
アカウントコード「1」を引数として AVERAGE_BNAD 関数を呼び出します。下記の実行例のよう
に、結果として「C」1 文字が返却されれば成功です。
実行例
$ cd /workshop/lab5
$ db2 -tvf call.average_band.clp
<省略>
DECLARE
str CHAR(30);
BEGIN
str := SALES.AVERAGE_BAND('A00',1);
DBMS_OUTPUT.PUT_LINE(str);
END;
DB20000I
SQL コマンドが正常に完了しました。
C
TERMINATE
DB20000I TERMINATE コマンドが正常に完了しました。
3.1.2
パッケージ仕様部
パッケージ仕様部の
仕様部の作成
ACCOUNT_PACKAGE のパッケージ仕様部を作成します。パッケージ仕様部は下記の様な
DDL となります。
10
ACCOUNT_PACKAGE パッケージ宣言部の DDL
CREATE OR REPLACE PACKAGE "SALES"."ACCOUNT_PACKAGE" AS
-- type declaration
TYPE customer_name_cache IS TABLE OF Employees%ROWTYPE INDEX BY PLS_INTEGER;
-- PROCEDURE declaration
PROCEDURE Add_Account(p_AccountId
p_DeptCode
p_AccountDesc
p_MaxEmployees
IN
IN
IN
IN
accounts.acct_id%TYPE,
accounts.dept_code%TYPE,
accounts.acct_desc%TYPE,
accounts.max_employees%TYPE);
PROCEDURE Remove_Account(p_AccountId IN accounts.acct_id%TYPE,
p_DeptCode IN accounts.dept_code%TYPE);
PROCEDURE Account_List(p_dept_code IN accounts.dept_code%TYPE,
p_acct_id
IN accounts.acct_id%TYPE,
p_Employees_Name_Cache OUT Customer_Name_Cache);
PROCEDURE Display_Account_List(p_dept_code IN
p_acct_id
IN
accounts.dept_code%TYPE,
accounts.acct_id%TYPE);
END Account_Package;
/
下記のコマンドを実行して、パッケージ仕様部を作成してください。
db2 -tvf db2plsql_package.db2.account_package
正常にパッケージ仕様部が作成されれば、「DB20000I SQL コマンドが正常に完了しました。」
がメッセージとして返却されます。ここでは作成の確認は行わず、パッケージ本体の作成に進
みます。
3.1.3
パッケージ本体
パッケージ本体の
本体の作成
パッケージ本体の DDL は、db2plsql_package_body.db2.account_package ファイルに格納されて
います。パッケージ本体についても、Oracle で稼働していた PL/SQL のロジックがそのまま使
用できます。ただし、パッケージ本体に含まれるプロシージャー中で参照している関数
(AVERAGE_BAND)の名前解決をするため、関数の検索順序に SALES スキーマを追加する必
要があります。
_パッケージ本体の DDL 修正
vi エディターから db2plsql_package_body.db2.account_package ファイルを開き、下記の DDL の
ように「SET CURRENT PATH = SALES, CURRENT PATH/」を追加してください。
ACCOUNT_PACKAGE パッケージ本体の DDL(一部抜粋)
11
$ cat db2plsql_package_body.db2.account_package
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
SET CURRENT PATH = SALES, CURRENT PATH
/
--#SET :PACKAGE_BODY:SALES:ACCOUNT_PACKAGE
CREATE OR REPLACE PACKAGE BODY "SALES"."ACCOUNT_PACKAGE" AS
PROCEDURE Add_Account(p_AccountId IN accounts.acct_id%TYPE,
p_DeptCode IN accounts.dept_code%TYPE,
p_AccountDesc
IN accounts.acct_desc%TYPE,
p_MaxEmployees IN accounts.max_employees%TYPE) IS
<省略>
_パッケージ本体の作成
修正が完了した後、下記のコマンドを使用してパッケージ本体を作成してください。
db2 -tvf db2plsql_package_body.db2.account_package
正常にパッケージ本体が作成されれば、「DB20000I SQL コマンドが正常に完了しました。」が
メッセージとして返却されます。
12
_パッケージの稼働確認
ACCOUNT_PACKAGE のメンバーの一つ、プロシージャーDisplay_Account_List を動かしてみる
ことで、パッケージの稼働確認を行います。/workshop/lab5 ディレクトリで下記のコマンドを実
行してください。
db2 -tvf call.display_account_list.clp
このコマンドは、下記の様なスクリプトを含んでいます。
CONNECT TO LABDB;
SET SQLCOMPAT PLSQL;
SET CURRENT SCHEMA SALES;
SET SERVEROUTPUT ON;
BEGIN
sales.account_package.Display_Account_List( 'C01', 3 );
END;
/
TERMINATE;
BEGIN/END で囲まれた部分が無名ブロックを構成しており、ここから Display_Account_List を
呼び出しています。
実行例
db2inst1@db2V97onSLES10:/> cd /workshop/lab5
db2inst1@db2V97onSLES10:/workshop/lab5> db2 -tvf call.display_account_list.clp
<省略>
SET SERVEROUTPUT ON
DB20000I SET SERVEROUTPUT コマンドが正常に完了しました。
BEGIN
sales.account_package.Display_Account_List( 'C01', 3 );
END;
DB20000I
SQL コマンドが正常に完了しました。
List of employees
---------------------------Record id
:
Employee
:
Number of projects
:
Average Band in department :
Record id
:
Employee
:
Number of projects
:
Average Band in department :
Record id
:
Employee
:
Number of projects
:
Average Band in department :
Record id
:
Employee
:
Number of projects
:
Average Band in department :
1
KWAN
1
C
2
QUINTANA
2
C
3
NICHOLLS
4
C
4
NATZ
1
C
TERMINATE
DB20000I TERMINATE コマンドが正常に完了しました。
上記のように、4 エントリー分の社員情報が表示されれば正常に稼働しています。
13
3.2
パッケージ OFFICE_PACKAGE の作成
このステップでは、パッケージ「OFFICE_PACKAGE」を作成します。このパッケージは OFFICE
表に対するデータの登録と削除を行うプロシージャーを含んでいます。パッケージのメンバー
は下記の 2 プロシージャーです。
Add_Office
Remove_Office
先ほど作成した ACCOUNT_PACKAGE では、PL/SQL のソースコードはそのまま使用できまし
たが、この OFFICE_PACKAGE は、そのままでは DB2 上で作成できない記述を含んでいます。
次ページに表示した、Remove_Office プロシージャーの青字部分がそれに該当します。
Remove_Office プロシージャーはパッケージ OFFICE_PACKAGE に属するパッケージですが、
自分自身の内部に別の関数 CHECK_EMP_EXISTANCE の宣言を含んでいます。
CHECK_EMP_EXISTANCE は、削除対象のレコードが存在するかどうかを確認する関数として
定義されており、Remove_Office プロシージャーからの内部からのみ参照できる関数です。
このように、関数やプロシージャーの内部で宣言される関数・及びプロシージャーを「ネストされ
た関数」、「ネストされたプロシージャー」と呼び、DB2 9.7 ではサポートされていません。
そのため、このような関数やプロシージャーを DB2 に対して作成するためには、ネストされた
関数やプロシージャー部分を分離して、独立した関数もしくはプロシージャーとして作成する必
要があります。独立したオブジェクトとして作成する際に考慮する必要があるのは下記の 2 点
です。
1. 関数名、プロシージャー名の重複
ネストされた関数、プロシージャーは、その定義を含む関数やプロシージャーの内部から
しか参照できません。そのため、例えば、別々のプロシージャーの中に定義されたネスト
された関数が同じ名前であったとしても問題ありません。独立したオブジェクトとして作成
する場合、一つのスキーマ配下に同じ名前の関数は一つしか存在できません。
そのため、どちらかの名前を変更する必要があります。また、名前を変更した関数につい
ては、呼び出し元のソースコードについても変更する必要があります。
2. 変数の共有について
ネストされた関数・プロシージャーと、その定義を含む関数やプロシージャーでは、変数を
共有することが可能です。ところが、独立したオブジェクト同士では変数の共有ができま
せん。
上記の課題を解決するために推奨される方策は、「パッケージへの変更」です。パッケージを使
用した場合、パッケージ仕様部のメンバーとしてグローバル変数を宣言することで、複数の関
数やプロシージャーから変数を共有することが可能です。
14
db2plsql_package_body.db2.office_package の抜粋(プロシージャー「Remove_Office」)
PROCEDURE Remove_Office(p_office_id
IN offices.office_id%TYPE) IS
-- /*
-- ||----------------------------------------------------------------------- || DESCRIPTION: Removes the Office from database based on the office id
-- || EXAMPLE: EXEC OFFICE_PACKAGE.REMOVE_OFFICE(1)
-- ||----------------------------------------------------------------------- */
-- variable declaration
v_existance number;
-- exception declaration
e_OfficeNotRegistered EXCEPTION;
PRAGMA EXCEPTION_INIT (e_OfficeNotRegistered, -20060);
-- /*
-- ||----------------------------------------------------------------------- || DESCRIPTION: Function declaration for checking
-- ||----------------------------------------------------------------------- */
FUNCTION CHECK_EMP_EXISTANCE ( p_office_Id IN offices.office_id%TYPE) RETURN NUMBER
AS
-- variable declaration
v_count
NUMBER;
v_flag
NUMBER;
BEGIN
SELECT count(*) INTO v_count from EMPLOYEES WHERE OFFICE_ID = p_office_Id;
IF ( v_count != 0 ) THEN
v_flag := 1;
ELSE
v_flag := 0;
END IF;
RETURN v_flag;
END CHECK_EMP_EXISTANCE;
BEGIN
-- Checking the parameter value
v_existance := CHECK_EMP_EXISTANCE (p_office_Id);
IF ( v_existance = 1 ) THEN
DBMS_OUTPUT.PUT_LINE('Office ID ' || p_Office_Id || ' has employee.');
RETURN;
END IF;
-- Execute deleting
DELETE FROM offices WHERE office_id = p_office_Id;
IF SQL%NOTFOUND THEN
RAISE e_OfficeNotRegistered;
END IF;
EXCEPTION
WHEN e_OfficeNotRegistered THEN
DBMS_OUTPUT.PUT_LINE('Office ' || p_Office_Id || ' does not exist.');
WHEN others THEN
RAISE e_OfficeNotRegistered;
END Remove_Office;
15
3.2.1
パッケージ仕様部
パッケージ仕様部の
仕様部の作成
前述の通り、OFFICE_PACKAGE を DB2 で作成するために、ネストされた関数
CHECK_EMP_EXISTANCE を、独立した関数に変更します。
パッケージ仕様部で宣言するメンバーは下記の様に変更となります。
変更前:
Add_Office(プロシージャー)
Remove_Office(プロシージャー)
変更後
Add_Office(プロシージャー)
Remove_Office(プロシージャー)
CHECK_EMP_EXISTANCE(関数)
_DDL の編集
viエディターで db2plsql_package.db2.office_package ファイルを開き、CHECK_EMP_EXISTANCE
関数の記述を追加してください。
変更例
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
--#SET :PACKAGE:SALES:OFFICE_PACKAGE
CREATE OR REPLACE PACKAGE "SALES"."OFFICE_PACKAGE" AS
-- PROCEDURE declaration
PROCEDURE Add_Office(p_office_id
p_building
p_number_seats
p_description
PROCEDURE Remove_Office(p_office_id
IN
IN
IN
IN
offices.office_id%TYPE,
offices.building%TYPE,
offices.number_seats%TYPE,
offices.description%TYPE);
IN offices.office_id%TYPE);
-- FUNCTION declaration
FUNCTION CHECK_EMP_EXISTANCE ( p_office_Id IN offices.office_id%TYPE) RETURN NUMBER;
END OFFICE_PACKAGE;
/
_パッケージ仕様部の作成
DDL の編集が完了した後に、下記のコマンドを使用してパッケージ仕様部を作成してください。
db2 -tvf db2plsql_package.db2.office_package
DB20000I メッセージが返却されれば、パッケージ仕様部の作成は成功です。
16
3.2.2
パッケージ本体
パッケージ本体の
本体の作成
次に、パッケージ本体を作成します。パッケージ本体では、Remove_Office プロシージャーから
ネストされた関数の記述を除去し、独立した関数として作成する必要があります。
_DDL の編集
vi エディターから db2plsql_package_body.db2.office_package ファイルを開き、下記の変更を行っ
てください。
・ Remove_Office プロシージャーからネストされた関数 CHECK_EMP_EXISTANCE の記述を
除去
・ Remove_Office プロシージャーと同列に、CHECK_EMP_EXISTANCE 関数のソースコードを
移動
次ページに変更後の DDL を抜粋して掲載します。青字となっている部分が新たに追加された
記述となります。また、Remove_Office プロシージャーから FUNCTION 定義が除去されている
ことがわかります。
17
db2plsql_package_body.db2.office_package の変更後の DDL(抜粋)
CREATE OR REPLACE PACKAGE BODY "SALES"."OFFICE_PACKAGE" AS
<省略>
PROCEDURE Remove_Office(p_office_id
IN offices.office_id%TYPE) IS
-- variable declaration
v_existance number;
-- exception declaration
e_OfficeNotRegistered EXCEPTION;
PRAGMA EXCEPTION_INIT (e_OfficeNotRegistered, -20060);
BEGIN
-- Checking the parameter value
v_existance := CHECK_EMP_EXISTANCE (p_office_Id);
IF ( v_existance = 1 ) THEN
DBMS_OUTPUT.PUT_LINE('Office ID ' || p_Office_Id || ' has employee.');
RETURN;
END IF;
-- Execute deleting
DELETE FROM offices WHERE office_id = p_office_Id;
IF SQL%NOTFOUND THEN
RAISE e_OfficeNotRegistered;
END IF;
EXCEPTION
WHEN e_OfficeNotRegistered THEN
DBMS_OUTPUT.PUT_LINE('Office ' || p_Office_Id || ' does not exist.');
WHEN others THEN
RAISE e_OfficeNotRegistered;
END Remove_Office;
-- /*
-- ||----------------------------------------------------------------------------- || DESCRIPTION: Function declaration for checking
-- ||----------------------------------------------------------------------------- */
FUNCTION CHECK_EMP_EXISTANCE ( p_office_Id IN offices.office_id%TYPE) RETURN NUMBER
AS
-- variable declaration
v_count
NUMBER;
v_flag
NUMBER;
BEGIN
SELECT count(*) INTO v_count from EMPLOYEES WHERE OFFICE_ID = p_office_Id;
IF ( v_count != 0 ) THEN
v_flag := 1;
ELSE
v_flag := 0;
END IF;
RETURN v_flag;
END CHECK_EMP_EXISTANCE;
END OFFICE_PACKAGE;
18
_パッケージ本体の作成
DDL の編集が完了した後に、下記のコマンドを使用してパッケージ本体を作成してください。
db2 -tvf db2plsql_package_body.db2.office_package
正常にパッケージ本体が作成されれば、DB20000I メッセージが返却されます。
_パッケージの稼働確認
下記のスクリプトを使用して、パッケージが正常に稼働しているかどうかを確認します。
cd /workshop/lab5
db2 -tvf call.office_package.clp
このスクリプトは下記の様な SQL を含んでおり、Add_Office プロシージャーと Remove_Office
プロシージャーを使用してオフィスの登録と削除を行います。
SELECT * FROM OFFICES;
BEGIN
sales.office_package.add_office( 3, '46 E. Main Street', 2, 'Temporary office in Hotel');
END
/
SELECT * FROM OFFICES;
BEGIN
sales.office_package.Remove_Office( 3);
END
/
SELECT * FROM OFFICES;
正常にパッケージが作成されている場合、次ページの実行例のようにオフィス ID=3 のオフィス
が追加・削除されることが確認できます。
19
OFFICE_PACKAGE の実行例
db2inst1@db2V97onSLES10:/workshop/lab5> db2 -tvf call.office_package.clp
<省略>
SELECT * FROM OFFICES
OFFICE_ID
--------1.
2.
BUILDING
NUMBER_SEATS DESCRIPTION
------------------ ------------ ----------------------------------------123 Offshore Drive
30. Main small office rented by the company
2348 Park Avenue
40. Additional facility for new hires
2 レコードが選択されました。
BEGIN
sales.office_package.add_office( 3, '46 E. Main Street', 2, 'Temporary office in
Hotel');
END
DB20000I
SQL コマンドが正常に完了しました。
Office 46 E. Main Street successfully registered .
SELECT * FROM OFFICES
OFFICE_ID
--------1.
2.
3.
BUILDING
NUMBER_SEATS DESCRIPTION
------------------ ------------ ----------------------------------------123 Offshore Drive
30. Main small office rented by the company
2348 Park Avenue
40. Additional facility for new hires
46 E. Main Street
2. Temporary office in Hotel
3 レコードが選択されました。
BEGIN
sales.office_package.Remove_Office( 3);
END
DB20000I
SQL コマンドが正常に完了しました。
SELECT * FROM OFFICES
OFFICE_ID
--------1.
2.
BUILDING
NUMBER_SEATS DESCRIPTION
------------------ ------------ ----------------------------------------123 Offshore Drive
30. Main small office rented by the company
2348 Park Avenue
40. Additional facility for new hires
2 レコードが選択されました。
20
4.
プロシージャーの
プロシージャーの作成
この章では以下の 3 つのプロシージャーの作成を行います。
COUNT_PROJECTS
ADD_NEW_EMPLOYEE
ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT
COUNT_PROJECTS は Oracle 上ではファンクションとして作成されていたオブジェクトです。
DB2 9.7 では、アウトパラメーターを持つファンクションをサポートしていない為、移行するには
アウトパラメーターを持つプロシージャーとして書き換える必要があります。
ADD_NEW_EMPLOYEE は、スタンドアロン・タイプを参照しており修正が必要ですが、1 章で既
に対応済みのため、ここでの修正は必要ありません。
ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT は修正の必要がなくそのまま作成が可能です。
4.1
COUNT_PROJECTS のパラメーター確認
パラメーター確認
_パラメーターの確認
最初に/workshop/lab5/plsql_ddl ディレクトリで以下のコマンドを実行し、COUNT_PROJECTS
ファンクションの DDL を確認します。
cat db2plsql_function.db2.count_projects
DDL の確認では、特にこのファンクションのパラメーターを確認します。上記のコマンドの実行
例の青字部分になります。
このファンクションでは、IN パラメーターと OUT パラメーターを 1 つずつ持ち、NUMBER の戻り
値を返します。
21
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl> cat db2plsql_function.db2.count_projects
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
--#SET :FUNCTION:SALES:COUNT_PROJECTS
CREATE OR REPLACE FUNCTION "SALES"."COUNT_PROJECTS"
"SALES"."COUNT_PROJECTS" (p_empID IN employees.emp_ID%TYPE, o_acct_id OUT
employees.acct_id%TYPE)
RETURN NUMBER AS
-- /*
-- ||-------------------------------------------------------------------------------------- || DESCRIPTION: Function that counts the project based on the employeed id and also
-- ||
returns information on total projects of the account to which employee id belongs
-- ||
-- || DEMO PURPOSE: Function with OUT parameter, FOR LOOP over cursor
-- ||
-- ||
-- || EXAMPLE: SELECT COUNT_PROJECTS(1, acct_id) FROM DUAL;
-- ||
-- ||-------------------------------------------------------------------------------------- */
-- variable declaration
v_TotalProjects
NUMBER:=0;
v_AccountProjects NUMBER;
<省略>
4.2
COUNT_PROJECTS のプロシージャーへの
プロシージャーへの書
への書き直し
db2plsql_function.db2.count_projects の以下の点を書き換えます。(修正したファイルは
/workshop/lab5/plsql_ddl_modified ディレクトリにあります。お時間のない方や vi の操作に不慣
れな方はこちらをご使用ください。)
8 行目 CREATE FUNCTION 文から CREATE PROCEDURE 文への変更と戻り値をアウトパラメ
ーターとして定義し直します。
変更前
CREATE OR REPLACE FUNCTION "SALES"."COUNT_PROJECTS" (p_empID IN
employees.emp_ID%TYPE, o_acct_id OUT employees.acct_id%TYPE) RETURN NUMBER
変更後
CREATE OR REPLACE PROCEDURE "COUNT_PROJECTS"
(p_empID IN employees.emp_ID%TYPE,
o_acct_id OUT employees.acct_id%TYPE,
out_return OUT NUMBER)
22
48 行目の RETURN をアウトパラメーターへの値の代入に書き換えます。
変更前
RETURN v_Totalprojects;
変更後
out_return:= v_Totalprojects;
修正例(青字部分が修正箇所)
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
--#SET :FUNCTION:SALES:COUNT_PROJECTS
CREATE OR REPLACE PROCEDURE "COUNT_PROJECTS"
(p_empID IN employees.emp_ID%TYPE,
o_acct_id OUT employees.acct_id%TYPE,
out_return OUT NUMBER)
AS
-- /*
・・・<省略>・・・
-- */
<省略>
BEGIN
FOR v_AccountRec IN c_DeptAccts LOOP
o_acct_id:=v_AccountRec.acct_id;
-- Determine the projects for this account.
SELECT num_projects
INTO v_AccountProjects
FROM accounts
WHERE dept_code = v_AccountRec.dept_code
AND acct_id = v_AccountRec.acct_id;
-- Add it to the total so far.
v_Totalprojects := v_Totalprojects + v_AccountProjects;
END LOOP;
out_return:= v_Totalprojects;
END Count_Projects;
/
TERMINATE
/
4.3
COUNT_PROJECTS プロシージャーの
プロシージャーの作成
_プロシージャーの作成
修正した DDL を以下のコマンドで実行し、プロシージャーを作成します。
db2 -tvf db2plsql_function.db2.count_projects
23
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl_modified> db2 -tvf db2plsql_function.db2.count_projects
CONNECT TO LABDB
データベース接続情報
データベース・サーバー
SQL 許可 ID
ローカル・データベース別名
= DB2/LINUX 9.7.0
= DB2INST1
= LABDB
SET CURRENT SCHEMA = 'SALES'
DB20000I SQL コマンドが正常に完了しました。
CREATE OR REPLACE PROCEDURE "COUNT_PROJECTS"
(p_empID IN employees.emp_ID%TYPE,
o_acct_id OUT employees.acct_id%TYPE,
out_return OUT NUMBER)
AS
<省略>
-- Add it to the total so far.
v_Totalprojects := v_Totalprojects + v_AccountProjects;
END LOOP;
out_return:= v_Totalprojects;
END Count_Projects;
DB20000I SQL コマンドが正常に完了しました。
TERMINATE
DB20000I TERMINATE コマンドが正常に完了しました。
CREATE OR REPLACE PROCEDURE 文の実行後に「DB20000I SQL コマンドが正常に完了し
ました」が表示されることを確認します。
_COUNT_PROJECTS プロシージャーの稼動確認
下記のスクリプトを使用して、パッケージが正常に稼働しているかどうかを確認します。
cd /workshop/lab5
db2 -tvf call.count_projects.clp
このスクリプトは下記の様な SQL を含んでおり、COUNT_PROJECTS プロシージャーを呼び出
してアウトパラメーターに返された値を表示します。
CONNECT TO LABDB;
SET SQLCOMPAT PLSQL;
SET CURRENT SCHEMA SALES;
SET SERVEROUTPUT ON;
DECLARE
i_emp_id NUMBER:=15;
o_acct_id employees.acct_id%TYPE;
24
o_num NUMBER;
BEGIN
SALES.COUNT_PROJECTS(i_emp_id, o_acct_id, o_num);
DBMS_OUTPUT.PUT_LINE ( 'Employee ID
: '|| i_emp_id);
DBMS_OUTPUT.PUT_LINE ( 'Related account ID: '|| o_acct_id);
DBMS_OUTPUT.PUT_LINE ( 'Total projects : '|| o_num);
END;
/
正常にプロシージャーが作成されている場合、実行時に以下の実行例のように
EmployeeID=15 の Related account ID と Total projects が表示されます。
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl_modified> cd /workshop/lab5
db2inst1@DB2V97onSLES10:/workshop/lab5> db2 -tvf call.count_projects.clp
CONNECT TO LABDB
データベース接続情報
データベース・サーバー
SQL 許可 ID
ローカル・データベース別名
= DB2/LINUX 9.7.0
= DB2INST1
= LABDB
SET SQLCOMPAT PLSQL
DB20000I SET SQLCOMPAT コマンドが正常に完了しました。
SET CURRENT SCHEMA SALES
DB20000I SQL コマンドが正常に完了しました。
SET SERVEROUTPUT ON
DB20000I SET SERVEROUTPUT コマンドが正常に完了しました。
DECLARE
i_emp_id NUMBER:=15;
o_acct_id employees.acct_id%TYPE;
o_num NUMBER;
BEGIN
SALES.COUNT_PROJECTS(i_emp_id, o_acct_id, o_num);
DBMS_OUTPUT.PUT_LINE ( 'Employee ID
: '|| i_emp_id);
DBMS_OUTPUT.PUT_LINE ( 'Related account ID: '|| o_acct_id);
DBMS_OUTPUT.PUT_LINE ( 'Total projects
: '|| o_num);
END;
DB20000I SQL コマンドが正常に完了しました。
Employee ID
: 15
Related account ID: 5
Total projects
: 4
TERMINATE
DB20000I TERMINATE コマンドが正常に完了しました。
25
4.4
ADD_NEW_EMPLOYEE プロシージャーの
プロシージャーの作成
ADD_NEW_EMPLOYEE プロシージャーは Oracle で稼働していた PL/SQL のロジックがそのま
ま使用できます。ただし、プロシージャー中で参照しているパッケージの変数
(ENVELOPE..EMP_INFO_TYPE,)の名前解決をするため、関数の検索順序に SALES スキーマ
を追加する必要があります。
_プロシージャーの DDL 修正
vi エディターから 1 章で修正した db2plsql_procedure.db2.add_new_employee ファイルを開き、下
記の DDL のように「SET CURRENT PATH = SALES, CURRENT PATH/」を追加してください。
(EMP_INFO_TYPE-> ENVELOPE.EMP_INFO_TYPE への変更は 1 章で対応済み)
db2plsql_procedure.db2.add_new_employee ファイル修正例(一部抜粋)
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
SET CURRENT PATH = SALES, CURRENT PATH
/
--#SET :PROCEDURE:SALES:ADD_NEW_EMPLOYEE
CREATE OR REPLACE PROCEDURE "SALES"."ADD_NEW_EMPLOYEE" (
p_FirstName employees.first_name%TYPE,
p_LastName employees.last_name%TYPE,
p_EmpMgrId employees.emp_mgr_id%TYPE,
p_DeptCode
employees.dept_code%TYPE,
p_Account employees.acct_id%TYPE,
o_Employee OUT ENVELOPE.EMP_INFO_TYPE,
p_CreateDate employees.create_date%TYPE DEFAULT SYSDATE,
p_OfficeId employees.office_id%TYPE DEFAULT 2
) AS
-- /*
-- ||-------------------------------------------------------------------------------------- || DESCRIPTION: Procedure to add a new employee
修正後、以下のコマンドを実行して ADD_NEW_EMPLOYEE プロシージャーを作成します。
db2 -tvf db2plsql_procedure.db2.add_new_employee
26
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl_modified> db2 -tvf db2plsql_procedure.db2.add_new_employee
CONNECT TO LABDB
データベース接続情報
データベース・サーバー
SQL 許可 ID
ローカル・データベース別名
= DB2/LINUX 9.7.0
= DB2INST1
= LABDB
SET CURRENT SCHEMA = 'SALES'
DB20000I SQL コマンドが正常に完了しました。
SET CURRENT PATH = SALES, CURRENT PATH
DB20000I SQL コマンドが正常に完了しました。
CREATE OR REPLACE PROCEDURE "SALES"."ADD_NEW_EMPLOYEE" (
p_FirstName employees.first_name%TYPE,
p_LastName employees.last_name%TYPE,
p_EmpMgrId employees.emp_mgr_id%TYPE,
p_DeptCode
employees.dept_code%TYPE,
p_Account employees.acct_id%TYPE,
o_Employee OUT ENVELOPE.EMP_INFO_TYPE,
p_CreateDate employees.create_date%TYPE DEFAULT SYSDATE,
p_OfficeId employees.office_id%TYPE DEFAULT 2
) AS
<省略>
OPEN c_get_employee;
FETCH c_get_employee INTO o_Employee;
CLOSE c_get_employee;
END Add_New_Employee;
DB20000I SQL コマンドが正常に完了しました。
_ADD_NEW_EMPLOYEE プロシージャーの稼動確認
下記のスクリプトを使用して、パッケージが正常に稼働しているかどうかを確認します。
cd /workshop/lab5
db2 -tvf call.add_new_employee.clp
このスクリプトは下記の様な SQL を含んでおり、ADD_NEW_ENPLOYEE プロシージャーを呼び
出し、EMPLOYEES 表への INSERT を実施します。EMPLOYEES 表に FIRST_NAME=’
MAX’のデータが挿入されるのが確認できるはずです。
CONNECT TO LABDB;
SET SQLCOMPAT PLSQL;
SET CURRENT SCHEMA SALES;
SET SERVEROUTPUT ON;
alter sequence employee_sequence restart with 999;
delete from employees where emp_id = 1000;
select emp_id,first_name,last_name,band from employees where first_name = 'MAX';
DECLARE
emp_info sales.ENVELOPE.EMP_INFO_TYPE;
27
BEGIN
sales.ADD_NEW_EMPLOYEE('Max', 'Trenton', 1, 'A00', 1, emp_info,sysdate);
DBMS_OUTPUT.PUT_LINE('EMP_ID
:' || emp_info.emp_id);
DBMS_OUTPUT.PUT_LINE('FIRST_NAME :' || emp_info.first_name);
DBMS_OUTPUT.PUT_LINE('LAST_NAME :' || emp_info.last_name);
DBMS_OUTPUT.PUT_LINE('BAND
:' || emp_info.band);
DBMS_OUTPUT.PUT_LINE('HIRE DATE :' || emp_info.hire_date);
END;
/
select emp_id,first_name,last_name,band from employees where first_name = 'MAX';
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5> db2 -tvf call.add_new_employee.clp
CONNECT TO LABDB
・・・<省略>・・・
alter sequence employee_sequence restart with 999
DB20000I SQL コマンドが正常に完了しました。
delete from employees where emp_id = 1000
DB20000I SQL コマンドが正常に完了しました。
select emp_id,first_name,last_name,band from employees where first_name = 'MAX'
EMP_ID FIRST_NAME
LAST_NAME
BAND
------- -------------------- -------------------- ---0 レコードが選択されました。
DECLARE
emp_info sales.ENVELOPE.EMP_INFO_TYPE;
BEGIN
sales.ADD_NEW_EMPLOYEE('Max', 'Trenton', 1, 'A00', 1, emp_info,sysdate);
DBMS_OUTPUT.PUT_LINE('EMP_ID
:' || emp_info.emp_id);
DBMS_OUTPUT.PUT_LINE('FIRST_NAME :' || emp_info.first_name);
DBMS_OUTPUT.PUT_LINE('LAST_NAME :' || emp_info.last_name);
DBMS_OUTPUT.PUT_LINE('BAND
:' || emp_info.band);
DBMS_OUTPUT.PUT_LINE('HIRE DATE :' || emp_info.hire_date);
END;
DB20000I SQL コマンドが正常に完了しました。
EMP_ID
FIRST_NAME
LAST_NAME
BAND
HIRE DATE
:1000
:MAX
:TRENTON
:1
:2009-09-12-06.17.03.000
select emp_id,first_name,last_name,band from employees where first_name = 'MAX'
EMP_ID FIRST_NAME
LAST_NAME
BAND
------- -------------------- -------------------- ---1000. MAX
TRENTON
1
1 レコードが選択されました。
28
4.5 ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT プロシージャー
の作成
ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT プロシージャーは Oracle で稼働していた PL/SQL
のロジックがそのまま使用できます。
以下のコマンドを実行して、ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT プロシージャーを作成し
ます。
db2 -tvf db2plsql_procedure.db2.assign_employee_to_new_account
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl_modified> db2 -tvf
db2plsql_procedure.db2.assign_employee_to_new_account
CONNECT TO LABDB
データベース接続情報
データベース・サーバー
SQL 許可 ID
ローカル・データベース別名
= DB2/LINUX 9.7.0
= DB2INST1
= LABDB
SET CURRENT SCHEMA = 'SALES'
DB20000I SQL コマンドが正常に完了しました。
CREATE OR REPLACE PROCEDURE "SALES"."ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT" (
p_EmployeeId IN employees.emp_id%TYPE,
p_DeptCode IN accounts.dept_code%TYPE,
p_AcctId IN accounts.acct_id%TYPE) AS
-- /*
-- ||-------------------------------------------------------------------------------------- || DESCRIPTION: Re-assigns employee to a new account
-- ||
-- ||
-- || DEMO PURPOSE: RAISE_APPLICATION_ERROR,
<省略>
EXCEPTION
WHEN NO_DATA_FOUND THEN
--Account information passed to this procedure doesn't exist. Raise an error
RAISE_APPLICATION_ERROR(-20001, p_DeptCode || ' ' || p_AcctId || ' doesn''t exist!');
END Assign_Employee_To_New_Account;
DB20000I SQL コマンドが正常に完了しました。
_ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT プロシージャーの稼動確認
下記のスクリプトを使用して、パッケージが正常に稼働しているかどうかを確認します。
cd /workshop/lab5
db2 -tvf call.assign_employee_to_new_account.clp
29
このスクリプトは下記の様な SQL を含んでいます。
正しくプロシージャーが作成されていれば、ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT プロシー
ジャーを呼び出しにより、EMP_ID=15 の DEPT_CODE が E21 に変更されるはずです。
CONNECT TO LABDB;
SELECT
SMALLINT(ACCT_ID) ACCT_ID, DEPT_CODE,
MAX_EMPLOYEES, CURRENT_EMPLOYEES
FROM ACCOUNTS WHERE DEPT_CODE in ('D11', 'E21');
SELECT
EMP_ID, FIRST_NAME, LAST_NAME, DEPT_CODE, ACCT_ID
FROM EMPLOYEES WHERE EMP_ID=15;
CALL SALES.ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT(15, 'E21', 4);
SELECT
SMALLINT(ACCT_ID) ACCT_ID, DEPT_CODE,
MAX_EMPLOYEES, CURRENT_EMPLOYEES
FROM ACCOUNTS WHERE DEPT_CODE in ('D11', 'E21');
SELECT
EMP_ID, FIRST_NAME, LAST_NAME, DEPT_CODE, ACCT_ID
FROM EMPLOYEES WHERE EMP_ID=15;
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5> db2 -tvf call.assign_employee_to_new_account.clp
・・・<省略>・・・
SELECT SMALLINT(ACCT_ID) ACCT_ID, DEPT_CODE, MAX_EMPLOYEES, CURRENT_EMPLOYEES FROM ACCOUNTS WHERE
DEPT_CODE in ('D11', 'E21')
ACCT_ID
------4
5
DEPT_CODE MAX_EMPLOYEES CURRENT_EMPLOYEES
--------- ------------- ----------------E21
3.
2.
D11
4.
4.
SELECT EMP_ID, FIRST_NAME, LAST_NAME, DEPT_CODE, ACCT_ID FROM EMPLOYEES WHERE EMP_ID=15
EMP_ID FIRST_NAME
LAST_NAME
DEPT_CODE ACCT_ID
------- -------------------- -------------------- --------- ------15. BRUCE
ADAMSON
D11
5.
CALL SALES.ASSIGN_EMPLOYEE_TO_NEW_ACCOUNT(15, 'E21', 4)
リターン状況 = 0
SELECT SMALLINT(ACCT_ID) ACCT_ID, DEPT_CODE, MAX_EMPLOYEES, CURRENT_EMPLOYEES FROM ACCOUNTS WHERE
DEPT_CODE in ('D11', 'E21')
ACCT_ID
------4
5
DEPT_CODE MAX_EMPLOYEES CURRENT_EMPLOYEES
--------- ------------- ----------------E21
3.
3.
D11
4.
3.
SELECT EMP_ID, FIRST_NAME, LAST_NAME, DEPT_CODE, ACCT_ID FROM EMPLOYEES WHERE EMP_ID=15
EMP_ID FIRST_NAME
LAST_NAME
DEPT_CODE ACCT_ID
------- -------------------- -------------------- --------- ------15. BRUCE
ADAMSON
E21
4.
30
5.
View の作成
この章では、以下の View を作成します。
ORGANIZATION_STRUCTURE"
この View の DDL では、Oracle 固有の FORCE オプションが指定されています。このオプション
は、View に依存関係を持つオブジェクトが View 作成時になくても View の作成を行えるように
するオプションです。
DB2 9.7 では FORCE オプションは CREATE VIEW 文で記述できないため、このオプションを取
り除いて実行する必要があります。
_DDL の確認
/workshop/lab5/plsql_ddl ディレクトリにある db2plsql_views.db2.organization_structure ファイル
の内容を以下のコマンドを実行して確認します。
cat db2plsql_views.db2.organization_structure
FORCE オプションがついていることを確認します。
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl> cat db2plsql_views.db2.organization_structure
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
--#SET :VIEW:SALES:ORGANIZATION_STRUCTURE
CREATE OR REPLACE FORCE VIEW "SALES"."ORGANIZATION_STRUCTURE" ("LEVEL", "FULL_NAME", "DEPARTMENT") AS
SELECT
LEVEL,
SUBSTR((LPAD(' ', 4 * LEVEL - 1) || INITCAP(e.last_name) || ', ' || INITCAP(e.first_name)), 1, 40),
NVL(d.dept_name, 'Uknown')
FROM
employees e,
departments d
WHERE
e.dept_code=d.dept_code(+)
START WITH emp_id = 1 CONNECT BY NOCYCLE PRIOR emp_id = emp_mgr_id
/
_FORCE オプションの除去
vi エディタを使用して db2plsql_views.db2.organization_structure ファイルを修正します。
(/workshop/lab5/plsql_ddl_modified ディレクトリに修正済みのファイルがあります。お時間のな
い方や vi の操作に不慣れな方はこちらをご使用ください。)
31
修正点
FORCE オプションを取り除く
修正例
--#SET TERMINATOR /
CONNECT TO LABDB
/
SET CURRENT SCHEMA = 'SALES'
/
--#SET :VIEW:SALES:ORGANIZATION_STRUCTURE
CREATE OR REPLACE VIEW "SALES"."ORGANIZATION_STRUCTURE" ("LEVEL", "FULL_NAME", "DEPARTMENT") AS
SELECT
LEVEL,
SUBSTR((LPAD(' ', 4 * LEVEL - 1) || INITCAP(e.last_name) || ', ' || INITCAP(e.first_name)), 1, 40),
NVL(d.dept_name, 'Uknown')
FROM
employees e,
departments d
WHERE
e.dept_code=d.dept_code(+)
START WITH emp_id = 1 CONNECT BY NOCYCLE PRIOR emp_id = emp_mgr_id
/
_View の作成
修正したファイルを以下のコマンドで実行します。
db2 -tvf db2plsql_views.db2.organization_structure
DB20000I メッセージが表示されトリガーが正常に作成されたことを確認します。
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl_modified> db2 -tvf db2plsql_views.db2.organization_structure
CONNECT TO LABDB
データベース接続情報
データベース・サーバー
SQL 許可 ID
ローカル・データベース別名
= DB2/LINUX 9.7.0
= DB2INST1
= LABDB
SET CURRENT SCHEMA = 'SALES'
DB20000I SQL コマンドが正常に完了しました。
CREATE OR REPLACE VIEW "SALES"."ORGANIZATION_STRUCTURE" ("LEVEL", "FULL_NAME", "DEPARTMENT") AS SELECT LEVEL,
SUBSTR((LPAD(' ', 4 * LEVEL - 1) || INITCAP(e.last_name) || ', ' || INITCAP(e.first_name)), 1, 40),
NVL(d.dept_name, 'Uknown') FROM employees e, departments d WHERE e.dept_code=d.dept_code(+) START WITH emp_id = 1
CONNECT BY NOCYCLE PRIOR emp_id = emp_mgr_id
DB20000I SQL コマンドが正常に完了しました。
32
6.
トリガーの
トリガーの作成
この章では Update_Departments,UPDATE_ACC_ON_NEW_EMPL の2つのトリガーを作成します。
6.1
Update_Departments トリガーの
トリガーの作成
Update_Departments トリガーは複数のアクションで起動するトリガーです。具体的には、
INSERT、UPDATE、DELETE 全ての変更処理に対して起動します。DB2 9.7 ではこのようなトリ
ガーをサポートしていません。DB2 で使用できるようにするためには、それぞれのアクションで
起動するトリガーとして分割して作成します。(例 INSERT トリガー、UPDATE トリガー、DELETE
トリガーの3つに分割)
_UPDATE_DEPARTMENTS トリガーの確認
最初に Update_DEPARTMENTS トリガーの DDL を確認します。
以下のコマンドを実行し、/workshop/lab5/pl_sql_ddl ディレクトリにある
db2plsql_trigger.db2.update_departments の内容を確認します。
cat db2plsql_trigger.db2.update_departments
青字になっている部分がトリガーの起動アクションの設定になります。
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl> cat db2plsql_trigger.db2.update_departments
・・・<省略>・・・
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_DEPARTMENTS"
・・・<省略>・・・
AFTER INSERT OR DELETE OR UPDATE ON employees
employees FOR EACH ROW
DECLARE
CURSOR c_Stats IS
SELECT dept_code, COUNT(*) total_employees,
SUM(current_projects) total_projects
FROM employees
GROUP BY dept_code;
BEGIN
IF DELETING THEN
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:old.dept_code;
ELSIF INSERTING THEN
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:new.dept_code;
ELSIF UPDATING THEN
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code IN (:old.dept_code, :new.dept_code);
END IF;
END UpdateDepartments;
/
33
ここでは、UPDATE_DEPARTMENTS トリガーを以下の3つのトリガーに分割して作成します。
UPDATE_DEPARTMENTS_ON_UPDATE
UPDATE_DEPARTMENTS_ON_INSERT
UPDATE_DEPARTMENTS_ON_DELETE
_UPDATE トリガーの作成
db2plsql_trigger.db2.update_departments ファイルを
db2plsql_trigger.db2.update_departments_update ファイルとしてコピーし以下の点を修正します。
(修正済みのファイルは、/workshop/lab5/plsql_ddl_modified に用意してあります。お時間がな
い方や vi の操作が不慣れな方はこちらをご使用ください。)
修正点
1・トリガー名の変更
UPDATE_DEPARTMENTS→ UPDATE_DEPARTMENTS_ON_UPDATE
2.トリガーの起動アクションの変更
AFTER INSERT OR DELETE OR UPDATE→ AFTER UPDATE
3.UPDATE 時以外の処理をコメントアウト
修正例(修正点は青字部分)
--#SET TERMINATOR /
・・・<省略>・・
--#SET :TRIGGER:SALES:UPDATE_DEPARTMENTS
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_DEPARTMENTS_ON_UPDATE
UPDATE_DEPARTMENTS_ON_UPDATE"
UPDATE_DEPARTMENTS_ON_UPDATE
-- /*
・・・<省略>・・・
-- */
AFTER UPDATE ON employees FOR EACH ROW
DECLARE
CURSOR c_Stats IS
SELECT dept_code, COUNT(*) total_employees,
SUM(current_projects) total_projects
FROM employees
GROUP BY dept_code;
BEGIN
-- IF DELETING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:old.dept_code;
-- ELSIF INSERTING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT count(1), SUM(current_projects) FROM employees) WHERE
WHERE dept_code=:new.dept_code;
-- ELSIF UPDATING THEN
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code IN
(:old.dept_code, :new.dept_code);
-- END IF;
END UpdateDepartments;
/
・・・
34
修正後、以下のコマンドで DDL を実行し、トリガーを作成します。
db2 -tvf db2plsql_trigger.db2.update_departments_update
DB20000I メッセージが表示されトリガーが正常に作成されたことを確認します。
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl_modified> db2 -tvf db2plsql_trigger.db2.update_departments_update
CONNECT TO LABDB
データベース接続情報
データベース・サーバー
SQL 許可 ID
ローカル・データベース別名
= DB2/LINUX 9.7.0
= DB2INST1
= LABDB
SET CURRENT SCHEMA = 'SALES'
DB20000I SQL コマンドが正常に完了しました。
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_DEPARTMENTS_ON_UPDATE"
・・・<省略>・・・
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code IN (:old.dept_code, :new.dept_code);
-- END IF;
END UpdateDepartments;
DB20000I SQL コマンドが正常に完了しました。
TERMINATE
DB20000I TERMINATE コマンドが正常に完了しました。
35
_INSERT トリガーの作成
db2plsql_trigger.db2.update_departments ファイルを
db2plsql_trigger.db2.update_departments_insert ファイルとしてコピーし以下の点を修正します。
(修正済みのファイルは、/workshop/lab5/plsql_ddl_modified に用意してあります。お時間がな
い方や vi の操作が不慣れな方はこちらをご使用ください。)
修正点
1・トリガー名の変更
UPDATE_DEPARTMENTS→ UPDATE_DEPARTMENTS_ON_INSERT
2.トリガーの起動アクションの変更
AFTER INSERT OR DELETE OR UPDATE→ AFTER INSERT
3.INSERT 時以外の処理をコメントアウト
修正例(修正点は青字部分)
・・・<省略>・・・
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_DEPARTMENTS_ON_INSERT
UPDATE_DEPARTMENTS_ON_INSERT"
UPDATE_DEPARTMENTS_ON_INSERT
-- /*
・・・<省略>・・・
-- */
AFTER INSERT ON employees FOR EACH ROW
DECLARE
CURSOR c_Stats IS
SELECT dept_code, COUNT(*) total_employees,
SUM(current_projects) total_projects
FROM employees
GROUP BY dept_code;
BEGIN
-- IF DELETING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:old.dept_code;
-- ELSIF INSERTING THEN
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:new.dept_code;
-- ELSIF UPDATING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code IN (:old.dept_code, :new.dept_code);
-- END IF;
END UpdateDepartments;
/
・・・
修正後、以下のコマンドで DDL を実行し、トリガーを作成します。
db2 -tvf db2plsql_trigger.db2.update_departments_insert
DB20000I メッセージが表示されトリガーが正常に作成されたことを確認します。
36
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl_modified> db2 -tvf db2plsql_trigger.db2.update_departments_insert
CONNECT TO LABDB
・・・<省略>・・・
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_DEPARTMENTS_ON_INSERT"
・・・<省略>・・・
BEGIN
-- IF DELETING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:old.dept_code;
-- ELSIF INSERTING THEN
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:new.dept_code;
-- ELSIF UPDATING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code IN (:old.dept_code, :new.dept_code);
-- END IF;
END UpdateDepartments;
DB20000I SQL コマンドが正常に完了しました。
・・・
37
_DELETE トリガーの作成
db2plsql_trigger.db2.update_departments ファイルを
db2plsql_trigger.db2.update_departments_delete ファイルとしてコピーし以下の点を修正します。
(修正済みのファイルは、/workshop/lab5/plsql_ddl_modified に用意してあります。お時間がな
い方や vi の操作が不慣れな方はこちらをご使用ください。)
修正点
1・トリガー名の変更
UPDATE_DEPARTMENTS→ UPDATE_DEPARTMENTS_ON_DELETE
2.トリガーの起動アクションの変更
AFTER INSERT OR DELETE OR UPDATE→ AFTER DELETE
3.DELETE 時以外の処理をコメントアウト
修正例(修正点は青字部分)
--#SET TERMINATOR /
・・・<省略>・・・
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_DEPARTMENTS_ON_DELETE
UPDATE_DEPARTMENTS_ON_DELETE"
UPDATE_DEPARTMENTS_ON_DELETE
-- /*
・・・<省略>・・・
-- */
AFTER DELETE ON employees FOR EACH ROW
DECLARE
CURSOR c_Stats IS
SELECT dept_code, COUNT(*) total_employees,
SUM(current_projects) total_projects
FROM employees
GROUP BY dept_code;
BEGIN
-- IF DELETING THEN
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:old.dept_code;
-- ELSIF INSERTING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:new.dept_code;
-- ELSIF UPDATING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code IN (:old.dept_code, :new.dept_code);
-- END IF;
END UpdateDepartments;
/
・・・
修正後、以下のコマンドで DDL を実行し、トリガーを作成します。
db2 -tvf db2plsql_trigger.db2.update_departments_delete
DB20000I メッセージが表示されトリガーが正常に作成されたことを確認します。
38
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl_modified> db2 -tvf db2plsql_trigger.db2.update_departments_delete
CONNECT TO LABDB
・・・<省略>・・・
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_DEPARTMENTS_ON_DELETE"
-- /*
・・・<省略>・・・
BEGIN
-- IF DELETING THEN
UPDATE departments SET (total_projects, total_employees)=
(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:old.dept_code;
-- ELSIF INSERTING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code=:new.dept_code;
-- ELSIF UPDATING THEN
-UPDATE departments SET (total_projects, total_employees)=
-(SELECT count(1), SUM(current_projects) FROM employees) WHERE dept_code IN
(:old.dept_code, :new.dept_code);
-- END IF;
END UpdateDepartments;
DB20000I SQL コマンドが正常に完了しました。
・・・
39
6.2
UPDATE_ACC_ON_NEW_EMPL トリガーの
トリガーの作成
UPDATE_ACC_ON_NEW_EMPL トリガーは、UPDATE_DEPARTMENTS トリガーのように複数の
操作に対して起動するトリガーではなく、INSERT に対してのみ起動するトリガーであり、DB2
9.7 でもそのまま稼動させることが可能です。
/workshop/lab5/plsql_ddl ディレクトリにある db2plsql_trigger.db2.update_acc_on_new_empl ファ
イルの DDL を以下のコマンドで確認します。
cat db2plsql_trigger.db2.update_acc_on_new_empl
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl> cat db2plsql_trigger.db2.update_acc_on_new_empl
・・・<省略>・・・
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_ACC_ON_NEW_EMPL"
・・・<省略>・・・
AFTER INSERT ON employees FOR EACH ROW
DECLARE
v_num_projects accounts.num_projects%TYPE;
BEGIN
-- Determine the number of projects for this account.
SELECT num_projects
INTO v_num_projects
FROM accounts
WHERE dept_code = :new.dept_code
AND acct_id = :new.acct_id;
UPDATE employees
SET current_projects = current_projects + v_num_projects
WHERE emp_id = :new.emp_mgr_id;
-- Add
UPDATE
SET
WHERE
AND
one to the number of employees in the project.
accounts
current_employees = current_employees + 1
dept_code = :new.dept_code
acct_id = :new.acct_id;
END Update_Acc_On_New_Empl;
/
以下のコマンドで DDL を実行し、トリガーを作成します。
db2 -tvf db2plsql_trigger.db2.update_acc_on_new_empl
DB20000I メッセージが表示されトリガーが正常に作成されたことを確認します。
40
実行例
db2inst1@DB2V97onSLES10:/workshop/lab5/plsql_ddl> db2 -tvf db2plsql_trigger.db2.update_acc_on_new_empl
・・・<省略>・・・
CREATE OR REPLACE TRIGGER "SALES"."UPDATE_ACC_ON_NEW_EMPL"
・・・<省略>・・・
-- Determine the number of projects for this account.
SELECT num_projects
INTO v_num_projects
FROM accounts
WHERE dept_code = :new.dept_code
AND acct_id = :new.acct_id;
UPDATE employees
SET current_projects = current_projects + v_num_projects
WHERE emp_id = :new.emp_mgr_id;
-- Add
UPDATE
SET
WHERE
AND
one to the number of employees in the project.
accounts
current_employees = current_employees + 1
dept_code = :new.dept_code
acct_id = :new.acct_id;
END Update_Acc_On_New_Empl;
DB20000I SQL コマンドが正常に完了しました。
以上です。
41
© Copyright IBM Corporation 2009
All Rights Reserved.
本書に含まれている情報は、正式なIBMのテストを受けていません。また、明記にしろ、暗黙的にしろ、なんらの保証もなしに配布され
るものです。
この情報の使用またはこれらの技術の実施は、いずれも、使用先の責任において行われるべきものであり、それらを評価し、実際に使用
する環境に統合する使用先の判断に依存しています。それぞれの項目は、ある特定の状態において正確であることがIBMによって調べら
れていますが、他のところで同じまたは同様の結果が得られる保証はありません。これらの技術を自身の環境に適用することを試みる使
用先は、自己の責任において行う必要があります。
42