Sharon L Hoffman著 小さくて呼び出しやすいコンポーネント単位にコードを分割することでモジュール性と再利用性が高まり、各コード・コンポーネントのサイズと複雑性も小さくなります。プログラム呼び出しやサービス・プログラムなどといったiSeriesのさまざまなプログラミング・テクニックを使用するとこういったタイプの設計をすることができますが、本稿ではトリガ、ストアード・プロシージャ、ユーザー定義関数(UDF)というアプリケーションの分割を容易にするiSeriesの3つのデータベース機能に着目します。 トリガは、ファイル中へのレコードの挿入などといったデータベースのイベントによって起動(あるいは発火)し、指定されたイベントが発生したときは必ず自動的に発火します。ストアード・プロシージャはSQLから明示的に呼び出され、一般的なiSeries言語におけるプログラム呼び出しと同様に動作します。UDFはSQL文中で起動され、CountやDayOfWeekなどといったSQLの組み込み関数と同様に動作します。 上記3つのデータベース機能により、RPGなどの高級言語で記述されたコードにSQLベースのアプリケーションがアクセスできるようになり、高級言語で記述されたアプリケーションがSQLの機能にアクセスできるようになるため、アプリケーション開発のオプションが広がります。トリガ、ストアード・プロシージャ、UDFの長所を利用すると、各タスクに対して最適なツール(SQLや高級言語)を使うことでコードを簡素化することができます。また、こうしたデータベース機能はいずれも複数のプラットフォームやデータベースでサポートされているため、これ以外の恩恵にもあずかることができます。 トリガ、ストアード・プロシージャ、UDFを使用してアプリケーションを分割することで、同じコードをさまざまなテクニックを使って起動できるようになります。たとえば、RPGで記述されたストアード・プロシージャを組み込みSQLを使ってCobolプログラム中で呼び出したり、JDBCを使用してJavaアプリケーション中で呼び出したりすることができます。ストアード・プロシージャを呼び出すコードはiSeries上で実行されているかもしれませんし、ODBC経由でiSeriesと通信している他のプラットフォーム上で実行されているかもしれません。同様に、SQLで記述されたトリガのコードはRPGプログラム中の更新、DFUなどのユーティリティ・プログラムによる変更の結果実行されるかもしれませんし、ブラウザ・ベースのアプリケーションによって起動されたトランザクションに対する応答として実行されるかもしれません。 こうしたデータベース・ソリューションを選択することでネットワーク・アプリケーションの信頼性と安全性が高まり、ほとんどの場合で性能も向上します。このような恩恵はデータベースが存在しているプラットフォームと同じプラットフォーム上に処理を移動させたことと、ネットワーク上のトラフィック量を低減させたことによる二重の結果によるものです。たとえばPC上で実行されているJavaアプリケーションからJDBCを使用して10個のSQL文をそれぞれ個別に呼び出す代わりに、10個のSQL文すべてを実行するような1つのストアード・プロシージャを呼び出してその結果を返すこともできます。転送する必要のあるデータ量は同じでも、ネットワーク上で送信されるトランザクション数を減らすことで性能を向上させることができます。また多くの場合、こうした設計をすることでやりとりされる情報量を低減させることができ、性能をさらに向上させることができます。 複数のプラットフォーム上に展開したいアプリケーションがある場合は、iSeries固有のアプリケーション分割ソリューションよりも上記のデータベース機能の方が有利です。たとえば、iSeriesのアプリケーション中でストアード・プロシージャを使用し、あとで他のデータベースに移行しようとする場合、アプリケーションの設計はそのまま、コードの呼び出しもほぼそのままで済む場合があります。 上記のデータベース・テクニックを使用すると、サービス・プログラムなどといったiSeries固有のソリューションを採用するよりもコードの移植性が高まりますが、トリガ、ストアード・プロシージャ、UDFの異なるデータベースやプラットフォーム上でのサポート方法には大きな違いがあります。たとえば、あるストアード・プロシージャを別のプラットフォームやデータベースに移行する場合、ストアード・プロシージャ自体を書き直すことになる可能性が高いでしょう。したがってiSeries固有のトリガ、ストアード・プロシージャ、UDFについて理解しておくことが重要です。 iSeriesでの実装の詳細 バージョンV5R1のOS/400では2つのタイプのトリガがサポートされています。SQLで記述されているSQLトリガと、RPG、Cobol、CLなどといったSQL以外の言語で記述されている外部トリガの2つです。外部トリガに対して通用するデータベース・イベントはレコードの挿入、更新、削除です。SQLトリガは外部トリガでサポートされているイベントに加え、特定の列が修正された場合だけに更新トリガを発火するように指定することができます。またSQLトリガは、修正された列ごとにトリガを1度発火させる代わりに、SQL文によって修正される列数にかかわらずSQL文ごとにトリガを1度発火させるなどといった、外部トリガでは利用できない機能も備えています。トリガはCreate Trigger SQL文を使用して定義するか、または外部トリガの場合だけですが、CrtPFTrg (Create Physical File Trigger) CLコマンドを使用して定義します。 iSeriesのDB2 UDBでは2つのタイプのストアード・プロシージャがサポートされています。SQLで記述されているSQLストアード・プロシージャとSQL以外の言語で記述されている外部ストアード・プロシージャの2つです。iSeriesのプログラム・オブジェクトを外部ストアード・プロシージャとしてアクセス可能にするためには、Create Procedure SQL文を使用してそのオブジェクトを登録しなければなりません。SQLストアード・プロシージャの場合、SQLのコードはCreate Procedure文の中に含まれています。ストアード・プロシージャを作成するとQsysroutinesとQsysparmsの2つのシステム・カタログ中にエントリが生成されます。 iSeriesでは2つのカテゴリのUDFがサポートされていて、戻り値の型によって識別されます。表UDFは結果セット(一時表)を返し、スカラーUDFは単一値を返します。表UDFとスカラーUDFの相違に加え、それぞれのタイプのUDFをSQL UDF、外部UDF、ソース派生UDFと分類することができます。トリガやストアード・プロシージャの場合と同様に、SQL UDFと外部UDFとの違いはUDFを記述するのに使用されている言語によります。 ソース派生UDFは組み込みSQL関数をベースとしており、カスタム・コードは含まれていません。ソース派生UDFはユーザー定義型(UDT)と一緒に使用するのがごく一般的です。たとえば、USCurrency型をDecimal SQL型をベースに定義する場合、組み込みのSQLSum関数をベースにSumUSCurrency UDFも作成しておいた方が良いでしょう。 UDFを定義する方法はストアード・プロシージャの定義の場合と同様です。3つのタイプのいずれのUDFもCreate Function SQL文を使用して定義します。外部UDFはCreate Function文で外部プログラムを指定します。ソース派生UDFは新しいUDFのベースとなる組み込み関数を指定します。SQL UDFでは、SQLコードがCreate Function文の中に含まれています。関数を作成する際は、Qsysroutinesシステム・カタログとQsysparmsシステム・カタログにエントリが追加されます。 トリガ、ストアード・プロシージャ、UDFを作成する一番簡単な方法はiSeries Navigatorにあるそれぞれ適切な「ウィザード」を使用することです。このウィザードは従来お馴染みのシーケンシャルなウィザードではなく、タブ・ペインが数枚つながっているものです。 テーマのバリエーション トリガ、ストアード・プロシージャ、UDFのいずれを使用しても、同じプログラミングの目標を達成することができる場合がほとんどです。しかし、トリガとストアード・プロシージャとUDFの間では呼び出しのメカニズムとパラメータの扱いが大きく異なります。これらの違いを理解しておくことで、特定のアプリケーションに対して上記の3つのテクニックのうちどれが最も適しているかを判断することができるようになります。 iSeriesのトリガは物理ファイルの属性の1つで、適当なデータベース・イベントが発生したときや物理ファイルが直接または間接的にアクセスされた時に発火します。データベースは指定されたデータベース・イベントが発生したときにアプリケーションから明示的な命令を受けなくてもそのトリガを起動します。 ストアード・プロシージャは、RPG、CL、その他の言語からプログラムやILEを呼び出すのと同じ方法でSQLから直接呼び出されます。iSeriesのストアード・プロシージャが作成されると、そのストアード・プロシージャはRPG、Cobol、CLなどの言語に組み込まれたSQL、JDBC経由のJava、その他のストアード・プロシージャからなどといった、ストアード・プロシージャ呼び出しをサポートしているすべてのSQLベースのインタフェースから呼び出すことができます。しかし、ストアード・プロシージャはiSeries NavigatorのRun SQLスクリプトなどといった対話型のSQLインタフェースを使用して呼び出すことはできません。 さまざまなプラットフォームやデータベース、プログラミング環境がストアード・プロシージャをサポートしていますが、これは複数のアプリケーションから呼び出される必要のある標準ルーチン(たとえば税金の計算のルーチン)などにとっては理想的なソリューションとなり得ます。たとえば、開発したJavaアプリケーションが他のプラットフォーム上で稼動している場合でもJDBCを使用してiSeriesのストアード・プロシージャを呼び出すことができます。さらに、クライアントをベースとしたデータ・アクセス用ツールのほとんどがストアード・プロシージャの呼び出しをサポートしています。 UDFがどのようにして起動されるのかを理解するには、SQLの組み込み関数の機能について考えてみるのが最も簡単な方法です。たとえば、組み込み関数を使用して日付データ型を数値データ型に変換できるのと同じで、UDFを記述してある型の通貨を別の型の通貨に変換することができます。同様に、SumやCountなどといった組み込み関数の動作を模擬したUDFの集合体を記述することもできます。もちろん、組み込み関数とはまったく関係のないUDFを記述することもできます。 UDFはiSeriesのあらゆるSQL環境で使用できます。たとえばアプリケーション内(組み込みSQL経由)でもその場限りのクエリーでも構いません。UDFを使用するには次に示すようにSQL文の中に入れればよいのです。 SELECT Hoffman.MyFunction01(OLQTY) AS "Total Quantity" from Hoffman.ORDLINE UDFは通常SQL文に出現するたびに繰り返し実行されます。唯一の例外はそのSQL文が単一の列(レコード)を処理する場合です。上に示した例は単一値を返しますが、OLQTYの値を処理する各列ごとに現在値に加えます。同様に、UDFを入れて数量と価格を積算するようにして増値をSelect文の一部として返す場合、処理する各列ごとに1度ずつ実行されます。この場合、戻り値(増値)は各列ごとに取り出すことができ、Select文が戻す結果集合の一部になります。 ご相談ください トリガ、ストアード・プロシージャ、UDFの2番目の大きな違いは引数の扱い方です。トリガ・プログラムは明示的にパラメータを送ったり受け取ったりすることはできませんので、トリガ・プログラムが利用できる情報に対して開発者は何もできません。しかし、トリガ・プログラムはそのトリガを発火させたデータベース・イベントのレコード・イメージにアクセスことができます。更新トリガはレコード・イメージの前後で受け取り、挿入トリガと削除トリガは後イメージだけ(挿入の場合)と前イメージだけ(削除の場合)を受け取ります。またトリガが利用できる情報にはそのトリガを起動したトランザクションに関する一般的な情報も含まれています。トリガはパラメータを直接送ったり受け取ったりすることができませんので、トリガを発火させるアプリケーションとトリガ・プログラムが通信を行う必要がある場合は、メッセージ、データ・キュー、データ・エリアなどのなんらかの外部メカニズムを使用する必要があります。 パラメータの処理については、本稿で述べた3つのテクニックの中でストアード・プロシージャが最も融通が利きます。ストアード・プロシージャは、多重結果セットを含む複数の入力パラメータを受け取り、複数の出力パラメータを返すことができます。ストアード・プロシージャにパラメータを渡すメカニズムはそのストアード・プロシージャを呼び出すのに使用するインタフェースに依存します。したがって、たとえば組み込みSQLを使用したRPGプログラムからの呼び出しは、JDBCのストアード・プロシージャ呼び出しとは若干異なります。 UDFは複数の入力パラメータを受け取って1つの値を返します。前述の例に示した通り、UDFの入力パラメータはUDFの直後に括弧でくくって指定し、SQL文の中でAS節を使用して戻り値を取り出すことができます。スカラーUDFの戻り値は単一の値です。表UDFでは戻り値は結果集合となります。UDFおよびそれがどのように起動されるかによって単一の戻り値となることもあればSQL文が処理する各列ごとに戻り値がある場合もあります。 通常は、関数の呼び出し方、必要なパラメータの種類、関数が呼び出される環境などに応じて、トリガ、ストアード・プロシージャ、UDFの3つのテクニックのいずれかを使用するかを決めます。 適切なツールの選択 アプリケーションを分割するのに上記3つのデータベース・ソリューションのどれを選択するかを決める際には次の一般的なルールを適用できます。 ・ データベース・イベントが発生するたびに何かを実行したい場合はトリガを使用する ・ コードの起動の仕方をコントロールしたいときや複数の出力パラメータが必要な場合はストアード・プロシージャを使用する ・ 列レベルの処理用や対話型のSQLインタフェースからコードを呼び出す必要があるときはUDFを使用する トリガの長所の1つは、指定されたデータベース・イベントが発生するたびにトリガが起動され、したがって開発者がこれをバイパスすることができないという点です。と同時に、トリガが持つこの特徴は欠点にもなりえます。トリガを呼び出す側のアプリケーションがトリガの存在に気づいていない可能性があるからです。その結果、トリガはデータの検証などといったアクティビティに使用するのではなく監査レコードの記述などといった自己完結型のプロセスに使用するのにとどめておく方が一般的には良いでしょう。データ検証はトリガとトリガを発火させるプログラムとの間の通信を必要とするからです。 トリガのもう1つの特徴は、これも長所でもあり欠点でもあるのですが、トリガは指定されたタイプのトランザクションが発生するたびに発火するということです。たとえば、夜間の処理の一部としてファイル中のすべてのレコードを更新し、更新トリガが発火するように割り当てられている場合、ファイル中のすべてのレコードに対してこのトリガが発火します。トリガ処理を停止する唯一の方法はこのトリガを削除して再作成することです。 V5R1でSQLトリガが追加されたことで、V3R1以降iSeriesのみで利用可能であったトリガが、それまでは非現実的であったプログラミング上の問題に対する実行可能なソリューションとなりました。たとえば、顧客マスター・ファイル用のトリガについて考えてみましょう。このファイル上では多くのアクティビティが発生しますが、ここでは顧客のステータス・コードの変更だけに着目します。外部トリガでは、更新トリガを割り当てられるにとどまり、これではどのような理由であれファイルが修正されるたびにトリガが発火します。一方、私たちのトリガ・プログラムでは、ステータス・コードに変更があったか否かをチェックし、変更がなければ処理を終了させることができます。しかしこの設計は性能の観点からは受け入れられません。顧客のステータス・コードが修正された場合にのみ発火するSQLトリガを使用すると性能を劇的に向上させることができる可能性があります。 本稿で述べた3つのデータベース機能のうち、ストアード・プロシージャが一番わかりやすい機能です。一般的に、サービス・プログラムやILEプロシージャ、プログラム呼び出しを使おうと考えるときはたいていストアード・プロシージャを使用することができます。ストアード・プロシージャは通常、トランザクションの検証、コミッションの計算、在庫量の確認などといったデータベース固有の大量の処理をする際に使用します。ストアード・プロシージャは明示的に呼び出され、入力パラメータも出力パラメータも受け付けるので、トリガよりもコントロールがしやすいでしょう。しかしストアード・プロシージャはトリガと違ってアプリケーション開発者がバイパスしたり見過ごしたりする可能性があります。したがって、トリガを使用してデータの完全性を保証するのと同様にストアード・プロシージャを使用することはできません。たとえばすべてのトランザクションに対して監査レコードが書かれることを保証したい場合は、ストアード・プロシージャではなくトリガを選択します。 UDFはSQL文が影響を及ぼすすべての列に対して少量の処理を繰り返したい場合に特に有用です。コードを毎回手で記述する代わりにUDFを使用することで、毎回同じ方法で計算が行われることを保証することができます。さらにUDFを使用することで、データベース間の非互換性を調整することができます。たとえば、現在のアプリケーションがOracleデータベースを使用し、iSeries用のUDB DB2にはない組み込みSQL関数を使用しているとしましょう。Oracleの組み込み関数と同じ名前のiSeriesのUDFを記述することで、アプリケーションをiSeriesに移行する処理を簡素化できます。 今後の行方 トリガ、ストアード・プロシージャ、UDFを使用するとアプリケーションのモジュール性とコードの再利用性を向上させることができます。この3つのテクニックはいずれもコード・コンポーネントを一度記述するだけでさまざまなインタフェースで利用することができます。つまり、変更が必要になったときに一度だけ変更すればよいのです。さらに、トリガとストアード・プロシージャとUDFはデータベースの標準機能であるため、複数のプラットフォームに対応するアプリケーションを開発する際や、ブラウザ・ベースのフロントエンドなどといったiSeriesのコンポーネントでないコンポーネントを含むアプリケーションを開発する際に高い柔軟性が得られます。 最後に、本稿で述べた3つの機能はいずれもiSeriesでは2つの種類で利用可能です。すなわちSQLトリガ、SQLストアード・プロシージャ、SQL UDFはSQLで記述され、外部トリガ、外部ストアード・プロシージャ、外部UDFはSQL以外の言語で記述されています。その結果、こうしたデータベース機能を使用してRPGなどの言語に対してSQL機能へのアクセスを許すことができ、RPG、Cobol、CLなどといった他の言語を介してのみ利用可能であるかあるいはそうした言語でより容易に利用可能な機能へアクセスできるようになります。 Sharon L. Hoffmanはベル・データ鰍フ提携先でiSeries NEWS誌を発行している米Penton Media, Inc.のシニア・テクニカル・エディタです。同氏は1981年にIBMのミッドレンジ・システムでキャリアをスタートし、技術教育の開発と実施だけでなく広範なアプリケーション開発をバックグラウンドとしています。同氏はCOMMONやその他の業界イベントで頻繁に講演しています。