スタッフブログ
satoです。
今回は普段あまり使われないと思うMySQLのストアドプロシージャについて解説したいと思います。
MySQL5.0以降ではストアドプロシージャが使えるようになっています。
通常MySQLでストアドを使うことはないとは思いますが、一連の、CREATE TABLE等とセットでDB処理を書く時などは便利です。
ストアドプロシージャは各RDBMSで独自に実装していることが多いのですが、MySQLは標準SQL準拠で実装されており、それなりに汎用性があるものになっています。とは言っても、PL/SQLとは違うので、どこで汎用的かと言われると難しいです。
PL/SQLとは違いがありますが、大枠としては似ている部分は多いと思います。
■ストアドプロシージャの作成
ストアドプロシージャの作成にはCREATE PROCEDURE命令を使います。
DELIMITER命令は、文の区切り文字(デリミタ)を変更します。
わかりづらいのですが、CREATE PROCEDURE命令は、「CREATE PROCEDURE」から「END;//」までが1つの文になり、その中で実行する命令を書くので、例えば、
と、DELIMITER無しにすると、「CREATE PROCEDURE」が「VALUES(f);」で終了してしまい、エラーになってしまいます。これを回避するために一時的にデリミタを//にしています。
これで定義した後に、
とすると、INSERT INTOが実行されるのがわかると思います。
■ストアドプロシージャの削除
削除にはDROP PROCEDURE文を使います。DROP TABLE等と同様に、IF EXISTSを付けることができるので、CREATEの前に書いておくと便利かと思われます。
■ストアドファンクション
戻り値が無いものをプロシージャと呼ぶのに対し、戻り値があるものはファンクションと呼びます。
基本的にはプロシージャと同様ですが、いくつか違う点があります。
・引数にinが不要
プロシージャの引数の先頭のinは、「この値は入力値です」というのを指示するためのもので、プロシージャの場合はoutという指定もできます。これはPHPで言うところのリファレンス渡しに近いものになります。
ファンクションの場合はRETURNで戻り値を指定するので不要です。
・戻り値の型の指定
CREATE FUNCTIONの引数リストの後に、「RETURNS int」というのがあります。ここで戻り値の型を指定します。(この場合はint)
・DETERMINISTIC
これを付けないで定義しようとすると、
入力値が同じ場合に出力値が同じ場合はDETERMINISTIC、そうでない場合はNOT DETERMINISTICを指定します。バイナリログの記録に関わるようです。
ファンクションはSELECTで呼び出します。
■変数の定義
内部で使う変数の定義にはDECLARE文を使います。
DECLARE文は全てBEGINの直後に書く必要があります。ルーチンの途中に書くことはできません。
のようなのはエラーになります。
変数に値を代入するにはSET文を使います。
■カーソル
ストアドでよく使う処理として、「SELECTの結果を1行毎に何か処理したい」ということだと思います。PHPのforeach的なものです。
これを実現するにはカーソルというものを使います。
カーソルは、SELECTの戻り値のポインタ的なもので、cursor型として定義します。
「DECLARE cr1 cursor FOR」の後に、そのカーソルで呼び出したいSQLを記述します。
「DECLARE continue handler FOR not found SET eod = 1;」は、カーソル呼び出しの際に、データが存在しなくなったら変数eodに1をセットするというhandlerを定義します。これでSELECTが終了したかどうかを判定します。
カーソルのDECLAREは通常変数のDECLAREの後に定義する必要があります。
のようにしてしまうと、
というエラーになります。
後はカーソルをOPENし、FETCHで値を取得するとINTOで指定した変数に代入され、それを処理します。
これを実行すると、
cが反映されているのがわかると思います。
その他、詳しくはMySQLのリファレンスマニュアルを参照してください。
今回は普段あまり使われないと思うMySQLのストアドプロシージャについて解説したいと思います。
MySQL5.0以降ではストアドプロシージャが使えるようになっています。
通常MySQLでストアドを使うことはないとは思いますが、一連の、CREATE TABLE等とセットでDB処理を書く時などは便利です。
ストアドプロシージャは各RDBMSで独自に実装していることが多いのですが、MySQLは標準SQL準拠で実装されており、それなりに汎用性があるものになっています。とは言っても、PL/SQLとは違うので、どこで汎用的かと言われると難しいです。
PL/SQLとは違いがありますが、大枠としては似ている部分は多いと思います。
■ストアドプロシージャの作成
ストアドプロシージャの作成にはCREATE PROCEDURE命令を使います。
DELIMITER // CREATE PROCEDURE test_proc(in f int) BEGIN INSERT INTO test_table(val) VALUES(f); END; // DELIMITER ;
DELIMITER命令は、文の区切り文字(デリミタ)を変更します。
わかりづらいのですが、CREATE PROCEDURE命令は、「CREATE PROCEDURE」から「END;//」までが1つの文になり、その中で実行する命令を書くので、例えば、
CREATE PROCEDURE test_proc(in f int) BEGIN INSERT INTO test_table(val) VALUES(f); END; ;
と、DELIMITER無しにすると、「CREATE PROCEDURE」が「VALUES(f);」で終了してしまい、エラーになってしまいます。これを回避するために一時的にデリミタを//にしています。
これで定義した後に、
CALL test_proc(1);
とすると、INSERT INTOが実行されるのがわかると思います。
■ストアドプロシージャの削除
削除にはDROP PROCEDURE文を使います。DROP TABLE等と同様に、IF EXISTSを付けることができるので、CREATEの前に書いておくと便利かと思われます。
■ストアドファンクション
戻り値が無いものをプロシージャと呼ぶのに対し、戻り値があるものはファンクションと呼びます。
DELIMITER // CREATE FUNCTION test_func(f int) RETURNS int DETERMINISTIC BEGIN RETURN (f + 2); END; // DELIMITER ;
基本的にはプロシージャと同様ですが、いくつか違う点があります。
・引数にinが不要
プロシージャの引数の先頭のinは、「この値は入力値です」というのを指示するためのもので、プロシージャの場合はoutという指定もできます。これはPHPで言うところのリファレンス渡しに近いものになります。
ファンクションの場合はRETURNで戻り値を指定するので不要です。
・戻り値の型の指定
CREATE FUNCTIONの引数リストの後に、「RETURNS int」というのがあります。ここで戻り値の型を指定します。(この場合はint)
・DETERMINISTIC
これを付けないで定義しようとすると、
ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)のようなエラーが発生する場合があります。
入力値が同じ場合に出力値が同じ場合はDETERMINISTIC、そうでない場合はNOT DETERMINISTICを指定します。バイナリログの記録に関わるようです。
SET GLOBAL log_bin_trust_function_creators = 1;で回避することもできます。
ファンクションはSELECTで呼び出します。
test@localhost[testdb]> select test_func(1); +--------------+ | test_func(1) | +--------------+ | 3 | +--------------+
■変数の定義
DROP FUNCTION IF EXISTS test_func2; DELIMITER // CREATE FUNCTION test_func2(f int) RETURNS int DETERMINISTIC BEGIN DECLARE ret int; SET ret = f + 2; RETURN ret; END; // DELIMITER ;
内部で使う変数の定義にはDECLARE文を使います。
DECLARE文は全てBEGINの直後に書く必要があります。ルーチンの途中に書くことはできません。
... BEGIN DECLARE ret int; SET ret = f + 2; DECLARE ret2 int; ...
のようなのはエラーになります。
変数に値を代入するにはSET文を使います。
■カーソル
ストアドでよく使う処理として、「SELECTの結果を1行毎に何か処理したい」ということだと思います。PHPのforeach的なものです。
これを実現するにはカーソルというものを使います。
DROP TABLE IF EXISTS test_table; CREATE TABLE test_table ( a int, b int, c int); INSERT INTO test_table(a, b, c) VALUES(1, 2, 0); INSERT INTO test_table(a, b, c) VALUES(2, 3, 0); INSERT INTO test_table(a, b, c) VALUES(3, 4, 0); DROP PROCEDURE IF EXISTS test_proc2; DELIMITER // CREATE PROCEDURE test_proc2() BEGIN DECLARE eod tinyint; DECLARE in_a, in_b int; DECLARE out_c int; DECLARE cr1 cursor FOR SELECT a, b FROM test_table; DECLARE continue handler FOR not found SET eod = 1; SET eod = 0; OPEN cr1; FETCH cr1 INTO in_a, in_b; WHILE eod = 0 DO SET out_c = in_a * in_b; UPDATE test_table SET c = out_c WHERE a = in_a AND b = in_b; FETCH cr1 INTO in_a, in_b; END WHILE; CLOSE cr1; END // DELIMITER ;
カーソルは、SELECTの戻り値のポインタ的なもので、cursor型として定義します。
「DECLARE cr1 cursor FOR」の後に、そのカーソルで呼び出したいSQLを記述します。
「DECLARE continue handler FOR not found SET eod = 1;」は、カーソル呼び出しの際に、データが存在しなくなったら変数eodに1をセットするというhandlerを定義します。これでSELECTが終了したかどうかを判定します。
カーソルのDECLAREは通常変数のDECLAREの後に定義する必要があります。
DECLARE cr1 cursor FOR SELECT a, b FROM test_table; DECLARE out_c int;
のようにしてしまうと、
ERROR 1337 (42000): Variable or condition declaration after cursor or handler declaration
というエラーになります。
後はカーソルをOPENし、FETCHで値を取得するとINTOで指定した変数に代入され、それを処理します。
これを実行すると、
test@localhost[testdb]> SELECT * FROM test_table; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 2 | 0 | | 2 | 3 | 0 | | 3 | 4 | 0 | +------+------+------+ 3 rows in set (0.01 sec) test@localhost[testdb]> CALL test_proc2(); Query OK, 1 row affected (0.01 sec) test@localhost[testdb]> SELECT * FROM test_table; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 2 | 2 | | 2 | 3 | 6 | | 3 | 4 | 12 | +------+------+------+ 3 rows in set (0.00 sec)
cが反映されているのがわかると思います。
その他、詳しくはMySQLのリファレンスマニュアルを参照してください。