ストアド プロシージャは、直接または間接に自身を参照することで、再帰プロシージャになることができます。つまり、定義しようとするプロシージャを起動するCALL文をストアド プロシージャ本体内に入れることができるということです。そのようなCALL文を入れ子にすることもできます。
作成しようとしているストアド プロシージャが、自身を直接参照または起動すると、SPLコンパイル警告(エラーではありません)とともにプロシージャが作成されます。参照されるオブジェクト(プロシージャ)は、まだ存在していないからです。
再帰のレベルに対する上限は特にありませんが、ストアド プロシージャの入れ子の限度である15回が適用されます。開かれたカーソルが存在すると、この限度はさらに引き下げられます。
相互再帰
また、相互に再帰するストアド プロシージャを作成することもできます。それは、ストアド プロシージャ本体内で互いに起動しあうプロシージャのことです。これは間接再帰です。どちらかのプロシージャの作成を試みると、SPLコンパイル警告が報告されます。これは、参照されているプロシージャが存在しないからです。
この警告が起きないようにするには、相手のプロシージャに対するCALL文を指定しないでまず一方のストアド プロシージャを作成してから、もう一方のストアド プロシージャを作成し、次にCALLの入った定義を備えた最初のプロシージャを後のプロシージャに置き換えます。
連鎖再帰
複数のプロシージャ(AがBを呼び出し、BがCを呼び出し、そしてCがAを呼び出す)を介した再帰連鎖になるように、相互再帰プロセスを拡張することができます。
例
最初の例は、自身を直接参照するストアド プロシージャの作成を示しています。ストアド プロシージャはコンパイル警告とともに作成されます。CALL文で起動されるプロシージャは、コンパイル時には存在していないからです。
2番目の例は、相互再帰を駆使し、コンパイル警告を出さないストアド プロシージャの作成を示しています。これは、新規のストアド プロシージャの作成や、既存のプロシージャのパラメータの変更に便利です。
例: 再帰処理
Employeeテーブルが存在すると想定します。
CREATE PROCEDURE spCall(INOUT empcode INTEGER, INOUT basic DECIMAL (6, 2)) BEGIN IF (empcode < 1005) THEN SELECT empbasic INTO basic FROM Employee WHERE empcode = empcode ; INSERT Temptab(empcode, basic); SET empcode = empcode + 1; CALL spCall(empcode, basic); END IF; IF (empcode = 1005) THEN SET empcode = empcode - 1; SELECT max(empbasic) INTO basic from Temptab; END IF; END;
ストアド プロシージャのコンパイル時に以下のようなコンパイル警告が発生しますが、プロシージャspCallは正常に作成されます。
SPL5000:W(L8), E(3807):Table/view/trigger/procedure ‘spCall’ does not exist.
ストアド プロシージャspCallを、spCall (1001, basic (title 'maximum'));としてはじめて起動したと想定します。
パラメータempcodeの引数として渡される値1001、1002、1003、および1004に関して最初のIF文の条件がtrueと評価されるため、ストアド プロシージャは自身を4回起動します。
例: 相互再帰
ユーザーU1が、ストアド プロシージャを作成しようとしていると想定します。この作成者はストアド プロシージャの直接所有者ではありません。Sp1とSp2はどちらもdb1データベース内に作成されるからです。
- 最初のストアド プロシージャSp1を再帰の指定なしに作成します。
CREATE PROCEDURE db1.Sp1(INOUT p1 INTEGER) BEGIN END;
- 既存のプロシージャdb1.Sp1を参照する2番目のプロシージャSp2を作成します。
CREATE PROCEDURE db1.Sp2(INOUT p1 INTEGER) BEGIN IF (p1 > 0) THEN CALL db1.Sp1(p1- 1); END IF; END;
- ストアド プロシージャSp1を、Sp2を参照するプロシージャに置き換えます。
REPLACE PROCEDURE db1.Sp1(INOUT p1 INTEGER) BEGIN IF (p1 > 0 ) THEN CALL db1.Sp2(p1 - 1); END IF; END;
関連トピック
相互再帰の詳細については、例: 相互再帰を参照してください。