データ セントリックへのステップ0
近頃は、データ セントリックな情報システムの必要性について耳にすることが多くなりました。それがあるべき姿なのでしょう。そうした場合、組織をサポートする適切な方法というのは、アプリケーション プログラムからロジックを取り出して、制約やトリガーのような手法を通じてデータベースへ移動することになるでしょうか。しかし、多くのショップがその方向へ向けて1歩目のステップを踏み出すことができるようになる前には、私が「ステップ0」と呼んでいる準備作業を行う必要があるのではないかと思われます。
データ セントリックなコンピューティングにおける「ステップ0」というのは、プログラムからハードコーディングされたデータ値を取り出して、データベースに移動することだと私は考えます。データベース マネージャーがビジネス ルール(たとえば、代金未払いのある顧客には出荷しないなど)を適用する必要があるのと同様に、特定のエンティティに対しては、データが駆動する形で特別なロジックを適用したりすることも必要になってくるのではないでしょうか。仕事中に、倉庫ID、顧客番号、デバイスID、ライブラリー名、出力キュー名、ユーザー プロファイルなどがハードコーディングされている例をよく目にします。わざわざ記事に取り上げるまでもないと思えるくらいに、ハードコーディングが珍しい存在であったらよいのですが、残念ながら、あちらこちらで目にしてしまうのが現状です。
以下では、ハードコーディングを判別する方法、ハードコーディングされたデータをデータベースへ移動する方法、およびデータにアクセスするのに必要となるプログラミングを簡素化する方法について説明します。
ハードコーディングを判別する
ハードコーディングされているデータを実際に目にすれば、ほとんどの人がそれと気付くだろうと思いますが、確信が持てないという方は、以下の質問について自問自答してみるとよいでしょう。
- このソフトウェアが他の会社で使用されるとした場合、この値は意味があるか。
- この値は、データベース表(物理ファイル)の主キーまたは主キーの一部であるか。
- この種類の値をもうひとつ追加することになった場合、いずれかのプログラムを修正および再コンパイルする必要が生じるか。
以下は、倉庫ID(WHSID)がハードコーディングされているRPGの例です。
C IF WHSID = 1
C EVAL WHSNAM = 'NEW YOLK'
C EVAL LEADTIME = 14
C EVAL MFGWHS = 'N'
C ELSE
C IF WHSID = 2
C EVAL WHSNAM = 'LOST ANGELES'
C EVAL LEADTIME = 7
C EVAL MFGWHS = 'Y'
C ENDIF
C ENDIF
質問 1: このソフトウェアが他の会社で使用されるとした場合、この値は意味があるか。
回答: いいえ。他の会社には、倉庫1や倉庫2がないかもしれません。いずれかのIDの倉庫があるとしても、おそらく所在地がNew YolkやLost Angelesであることはなく、リード タイムもおそらく異なるでしょうし、それらの倉庫がメーカーの倉庫であるかどうかという点も、おそらく異なるでしょう。
質問 2: この値は、データベース表(物理ファイル)の主キーまたは主キーの一部であるか。
回答: 場合によります。倉庫マスター ファイルがある場合は「はい」であり、その場合は倉庫IDが主キーになるでしょう。
質問 3: この種類の値をもうひとつ追加することになった場合、いずれかのプログラムを修正および再コンパイルする必要が生じるか。
回答: 会社が新たな倉庫を開設することになったとしたら、このプログラムも、倉庫IDをテストする他のプログラムも修正しなければならなくなるでしょう。
結論としては、倉庫IDはハードコーディングするべきでない、ということになるでしょう。
では、次のコードを見てみてください。
C IF CUSCLASS = 'A'
. . . do whatever
C ENDIF
このアプリケーションでは、顧客が、A(上得意客)、B(常連ではないが失いたくない顧客)、またはC(他社と取引するようになってもらいたい顧客)に分類されます。Aをハードコーディングすることに、意味はあるでしょうか。はい、意味があります。上得意客というのはどの企業にもいるため、このような分類を行うことをソフトウェアの1つの機能とすることができるとしたら、意味があると言えます。また、顧客の種類は主キーではなく、顧客の属性であるという点にも注意しておいてください。
新たな属性を判別する
属性は、はっきりそれとは分からないことがよくあります。つまり、属性は存在しているのですが、それが属性であるとは分かりづらいということです。たとえば、次のような例があるとします。
C IF WHSID = 1 OR
C WHSID = 5 OR
C WHSID = 12
. . . calculate lead time one way
C ELSE
C IF WHSID = 2 OR
C WHSID = 6
. . . calculate lead time a different way
C ELSE
. . . calculate lead time using yet another way
C ENDIF
C ENDIF
このコードは、倉庫ID別に異なる方法の計算を処理させるという点が最初の例と異なります。つまり、前の例ではすでに割り当てられていたリード タイムを、それぞれ異なる計算方法を用いて算出する処理を行うということです。使用する計算方法は3種類あります。それらにA、B、Cや1、2、3といったコード(見分けられれば何でもよい)を割り当てて、適切な倉庫表に、リード タイム計算方法の列(フィールド)を追加することが必要になってきます。そのようにして修正したコードを以下に示します。
C WHSID CHAIN WHSMAST
. . . handle not found
C IF LTMETHOD = 'A'
. . . calculate lead time one way
C ELSE
C IF LTMETHOD = 'B'
. . . calculate lead time a different way
C ELSE
. . . calculate lead time using yet another way
C ENDIF
C ENDIF
データベースへ移動する
リレーショナル データベースでは、データは表(物理ファイル)に格納されます。ハードコーディングされたデータを表に移動する手法として、次の3つの選択肢があります。
- データの格納に適切な表がない場合は、表を作成する。
- データの格納に適切な表がある場合は、属性を格納するための新たな列(フィールド)を追加する。
- 適切な表があるが、新たな列を追加することができない場合は、同じキーを持つ補助表を作成する。
では、これらについて順番に見て行きましょう。
多くの表を作成した経験をお持ちでしょうから、表の作成方法の説明は不要と思われますが、念のため、warehouses(倉庫)マスター表を作成するコマンドを以下に示しておきます。
create table warehouses
( ID dec(3)
primary key,
Name varchar(20),
LeadTime dec(3)
not null with default 0,
IsManufacturing for MfgWhs char(1)
not null with default 'N'
check (IsManufacturing in ('Y', 'N'))
)
insert into warehouses values
( 1, 'New Yolk', 14, 'Y'),
( 2, 'Lost Angeles', 7, 'N')
DDSを使用して物理ファイルを作成することもできますが、私のお勧めはSQLです。
倉庫マスターがすでにある場合は、新たな列を追加することを考えます。
alter table warehouses
add column Name varchar(20)
add column LeadTime dec(3)
not null with default 0
add column IsManufacturing for MfgWhs char(1)
not null with default 'N'
check (IsManufacturing in ('Y', 'N') )
場合によっては、新たな列の追加が無理なケースもあります。ソフトウェア パッケージを実行しているショップでありがちなケースです。ソフトウェア ベンダー製の表を修正するという選択肢はありません。そのようなケースでは補助表を作成します。マスター ファイルの新規作成で使用した同じSQLステートメントを使用します。上の例を参照してください。2つの表は同じキー フィールドを持つことになります。マスター表にあるレコードが、必ず補助表にもあるというわけではないため、2つの表の結合は、一般に、マスター表が主となる外部結合となります。
これでデータが適切な表に入れば、最初の例のプログラムを修正して、表にランダム読み取り(RPG用語ではCHAIN)を行って適切なデータ値を取得できるようになります。
プログラミングを簡素化する
以下に、ハードコーディングされたデータが使用される、よくあるパターンをもうひとつ示します。ここでは、CLプログラムです。Nosmo King、Malcolm Oron、およびDonald Truckというユーザーが、レポートに対して手を加える内容です。
DCL VAR(&USER) TYPE(*CHAR) LEN(10)
RTVJOBA USER(&USER)
IF COND(&USER = 'NKING') THEN(OVRPRTF +
FILE(SUPERPRTF) PRTTXT('Nosmo King') +
OUTQ(PRODCTL3))
ELSE CMD(IF COND(&USER = 'MORON') THEN(OVRPRTF +
FILE(SUPERPRTF) OUTQ(ACCTSPAY)))
ELSE CMD(IF COND(&USER = 'DTRUCK') THEN(OVRPRTF +
FILE(SUPERPRTF) PRTTXT('Donald Truck') +
COPIES(2)))
CALL PGM(SUPERPGM)
DLTOVR FILE(SUPERPRTF)
このような例は数多く目にしてきました。読者の皆さんも同じだろうと思います。どのようにしたら、このオーバーライド情報をデータベースに移動できるでしょうか。1つ方法があります。
まずは、オーバーライド コマンドの表を作成します。
create table overrides
( UserID char(10),
ReportID char(10),
Override varchar(350) not null with default,
primary key (UserID, ReportID))
insert into overrides values
('NKING','SUPERPRTF','PRTTXT(''Nosmo King'') OUTQ(PRODCTL3)'),
('MORON','SUPERPRTF','OUTQ(ACCTSPAY)'),
('DTRUCK','SUPERPRTF','PRTTXT(''Donald Truck'') COPIES(2)')
オーバーライド情報(overrides)は、データベース内にあります。どのようにしてアクセスしたらよいでしょうか。
そのようなCLプログラムすべてで、ファイル宣言(DCLF)およびファイル受信(RCVF)コマンドを追加することもできますが、そんなことをしても何にもなりません。私は、それらのサンプル コードをお見せしようという気にさえならないくらいです。より良い方法は、次のGETOVRの例のような、オーバーライド情報(overrides)を取得するプログラムを書くことです。
ctl-opt option(*srcstmt: *nodebugio);
dcl-f overrides keyed rename(overrides: OvrRec);
dcl-pr GETOVR extpgm('GETOVR');
inUser char(10) const;
inReport char(10) const;
ouOverride char(256);
end-pr GETOVR;
dcl-pi GETOVR;
inUser char(10) const;
inReport char(10) const;
ouOverride char(256);
end-pi GETOVR;
chain (inUser: inReport) OvrRec;
if %found();
ouOverride = Override;
else;
ouOverride = *blanks;
endif;
return;
このプログラムを呼び出すように、CLプログラムを修正します。
DCL VAR(&USER) TYPE(*CHAR) LEN(10)
DCL VAR(&OVERPARMS) TYPE(*CHAR) LEN(350)
DCL VAR(&OVERRIDE) TYPE(*CHAR) LEN(350)
DCL VAR(&OLENGTH) TYPE(*DEC) LEN(15 5) VALUE(350)
RTVJOBA USER(&USER)
CALL GETOVR (&USER SUPERPRTF &OVERPARMS)
IF COND(&OVERPARMS *NE ' ') THEN(DO)
CHGVAR &OVERRIDE ('OVRPRTF SUPERPRTF' *BCAT &OVERPARMS)
CALL QCMDEXC (&OVERRIDE &OLENGTH)
ENDDO
CALL PGM(SUPERPGM)
IF COND(&OVERPARMS *NE ' ') +
THEN(DLTOVR FILE(SUPERPRTF))
CALLの追加は、I/Oコマンドよりはるかに簡単です。
ALTER TABLE EMPLOYEE
ADD CONSTRAINT C_HIRE_AGE_ERR0002
CHECK( YEAR(HIRE_DATE-BIRTH_DATE) >= 16 ) ;
すべての制約の名前がロング ネームであることにお気付きでしょうか。制約を作成しても、対応するシステム オブジェクトが作成されるわけではないため、10文字の名前であることは求められません。
このアイデアは私の発案ではありません。使われているところを何度も見たことがあります。実際、非常に気に入って使っている人もいるようです。たとえば、私が使用してきたシステムでは、1つの長いコマンド文字列の代わりに、すべてのオーバーライド パラメーターに対して別々のフィールドを定義しました。また、キー フィールドに汎用値や*ALLのような特殊値が許容されることもよくあります。しかし、例で使用した最小限のコードでさえ、プログラムからデータを取得して、データベースのしかるべき場所に入れることはできたわけです。
あなたのショップがすでにデータセントリックになっているのであれば素晴らしいことです。今なおその方向性へ進みつつあるならお見事です。まだ「ステップ0」を行っていないのであれば、すぐに取り掛かってください。