ストアドプロシージャで複数ResultSetを取得

S2JDBCで地味に便利なのがストアド周りです。

たとえば、Oracleで3つのカーソルを返すストアドプロシージャを用意します。

create or replace PROCEDURE PROC 
( cur1 OUT SYS_REFCURSOR, 
  cur2 OUT SYS_REFCURSOR,
  cur3 OUT SYS_REFCURSOR
) AS
BEGIN
  OPEN cur1 FOR SELECT * FROM EMPLOYEE WHERE EMPLOYEE_ID < 10; 
  OPEN cur2 FOR SELECT * FROM DEPARTMENT WHERE DEPARTMENT_ID < 10; 
  OPEN cur3 FOR SELECT * FROM ADDRESS WHERE ADDRESS_ID < 10; 
END PROC;

これはこんな感じで呼び出せます。

public void testProcedure() throws Exception {
    Param param = new Param();
    jdbcManager.call("PROC", param).execute();

    assertNotNull(param.employees);
    assertTrue(param.employees.size() > 0);
    assertNotNull(param.departments);
    assertTrue(param.departments.size() > 01);
    assertNotNull(param.addresses);
    assertTrue(param.addresses.size() > 0);
}

public static class Param {

    @ResultSet
    public List<Employee> employees;

    @ResultSet
    public List<Department> departments;

    @ResultSet
    public List<Address> addresses;
}

ポイントはパラメータ用のクラスに結果セットに対応するListを用意して@ResultSetをつけておくこと。一度に複数の結果セットを取得できちゃうのが便利。ストアド好きの人は結構うれしんじゃないでしょうか。

ちなみに、RDBMSごとに結果セットを返すストアドの書き方はぜんぜん違うのです(結果セットを返さなくてもちがうけど)。S2JDBCのテストをするときに、OracleSQL ServerDB2MySQLPostgreSQLと5つのDB用のストアドプロシージャを書いたのですが、はっきり言ってどれも覚えてない。。。。SVNにコードがあるから見ればわかりますけど。

次のストアドプロシージャのコードをぱっと見てどのRDBMSのものかわかったらすごいです。

CREATE OR REPLACE PROCEDURE PROC_RESULTSETS
( empCur OUT SYS_REFCURSOR, 
  deptCur OUT SYS_REFCURSOR, 
  employeeId IN NUMERIC, 
  departmentId IN NUMERIC
) AS
BEGIN
  OPEN empCur FOR SELECT * FROM EMPLOYEE WHERE employee_id > employeeId ORDER BY employee_id;  
  OPEN deptCur FOR SELECT * FROM DEPARTMENT WHERE department_id > departmentId ORDER BY department_id;
END PROC_RESULTSETS;
/
CREATE PROCEDURE dbo.PROC_RESULTSETS
    @employeeId int,
    @departmentId int
AS
BEGIN
    SELECT * FROM EMPLOYEE WHERE employee_id > @employeeId ORDER BY employee_id;  
    SELECT * FROM DEPARTMENT WHERE department_id > @departmentId ORDER BY department_id;
END
GO
CREATE PROCEDURE PROC_RESULTSETS(
  IN employeeId INTEGER,
  IN departmentId INTEGER)
DYNAMIC RESULT SETS 2
BEGIN
  DECLARE c_emp CURSOR WITH RETURN FOR
    SELECT * FROM EMPLOYEE WHERE employee_id > employeeId ORDER BY employee_id;
  DECLARE c_dept CURSOR WITH RETURN FOR
    SELECT * FROM DEPARTMENT WHERE department_id > departmentId ORDER BY department_id;
  OPEN c_emp;
  OPEN c_dept;
END@
CREATE PROCEDURE PROC_RESULTSETS(
  IN employeeId INTEGER,
  IN departmentId INTEGER)
BEGIN
    SELECT * FROM EMPLOYEE WHERE employee_id > employeeId ORDER BY employee_id;  
    SELECT * FROM DEPARTMENT WHERE department_id > departmentId ORDER BY department_id;
END
/
CREATE OR REPLACE FUNCTION PROC_RESULTSETS(
  empCur OUT refcursor,
  deptCur OUT refcursor,
  employeeId IN INTEGER,
  departmentId IN INTEGER)
AS $$
BEGIN
  OPEN empCur FOR SELECT * FROM EMPLOYEE WHERE employee_id > employeeId ORDER BY employee_id;
  OPEN deptCur FOR SELECT * FROM DEPARTMENT WHERE department_id > departmentId ORDER BY department_id;
  RETURN;
END;
$$ language plpgsql;

答えは、上から順にOracleSQL ServerDB2MySQLPostgreSQLとなります。カーソルの定義の仕方、パラメータのタイプ(INとかOUT)を書く位置、区切り文字などもそれぞれ異なっていてとても覚えられないですよねー。