ストアドプロシージャ
出典: フリー百科事典『ウィキペディア(Wikipedia)』
ストアドプロシージャ (stored procedure) とは、データベースに対する一連の処理をまとめた手続きにし、リレーショナルデータベースマネージメントシステムに保存(永続化)したもの。
目次 |
[編集] 概要
標準SQLでは、SQL/PSM規格として策定されている。 ベンダー各社とも、静的・動的SQLにカーソル処理や制御構文・例外処理などを含めた仕様の拡張言語により手続きを記述することができるDBMSを提供している場合が多い。また、C言語で記述しコンパイルした外部モジュール(共有ライブラリなど)やJavaのクラスライブラリから、関数やクラスメソッドを呼び出すことで実現する「外部プロシージャ」機能を実装しているものもある。
ストアドプロシージャを利用することにより次のようなメリットが生まれる。
- ひとつの要求で複数のSQL文を実行できる(ネットワークに対する負荷を軽減できる)
- あらかじめ構文解析や内部中間コードへの変換をすませるため、処理時間が軽減される
- データベーストリガーと組み合わせることで、複雑なルールによるデータの参照整合性保持が可能になる。簡単に言えば、アプリケーション側にロジックを持たずとも、データベース内のデータのつじつまが合うようにすることができる。
ストアドプロシージャを多用することによるデメリットには以下のようなものがある。
- データベース製品ごとに、記述する構文の規約がSQL/PSM規格との互換性が低いため、コード資産としての再利用性が悪い。
- 処理の都合上、アプリケーションのビジネスロジックを組みこまなければならない場合、業務の仕様変更が起こった場合、外部のアプリケーションとともにストアドプロシージャも変更しなければならないため、よけいな手間や変更ミスによる障害を発生させる場合もあるため、メンテナンス性が悪くなる。
[編集] MySQLのストアドプロシジャ
[編集] 概要
MySQLのストアドプロシジャは、バージョン5.0以降に標準SQL準拠でサポートされている。 ファンクションとプロシジャで、同等の処理を行う例を、以降に示す。
[編集] ファンクションの例1(DB操作なし)
(1)定義
drop function if exists DecToNshin; -- 存在したら削除 delimiter // -- 終端記号の変更 create function DecToNshin (dec_num int, n_shin tinyint) returns varchar(32) -- -- 10進数→n進数 -- begin declare ltr char(36) default '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; declare w_dec_num int; declare amari int; declare w_DecToNshin varchar(32); set w_DecToNshin=''; set w_dec_num=dec_num; while w_dec_num>=n_shin do set amari=mod(w_dec_num,n_shin); set w_DecToNshin=concat(substr(ltr,amari+1,1),w_DecToNshin); set w_dec_num=w_dec_num div n_shin; end while; set w_DecToNshin=concat(substr(ltr,w_dec_num+1,1),w_DecToNshin); return w_DecToNshin; end; // delimiter ; -- 終端記号を元に戻す
(2)実行
SELECT DecToNshin(100,16); -- 100を16進数にすると?
[編集] プロシジャの例1(DB操作なし)
(1)定義
drop procedure if exists DecToNshin; -- 存在したら削除 delimiter // -- 終端記号の変更 create procedure DecToNshin (in dec_num int, in n_shin tinyint, out w_DecToNshin varchar(32)) -- -- 10進数→n進数 -- begin declare ltr char(36) default '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; declare w_dec_num int; declare amari int; set w_DecToNshin=''; set w_dec_num=dec_num; while w_dec_num>=n_shin do set amari=mod(w_dec_num,n_shin); set w_DecToNshin=concat(substr(ltr,amari+1,1),w_DecToNshin); set w_dec_num=w_dec_num div n_shin; end while; set w_DecToNshin=concat(substr(ltr,w_dec_num+1,1),w_DecToNshin); end; // delimiter ; -- 終端記号を元に戻す
(2)実行
CALL DecToNshin(100,16,@RSLT); -- 100を16進数にすると? SELECT @RSLT; -- 結果の表示
[編集] ファンクションの例2(DB操作あり)
(1)表定義及びデータ
create table gengou (bgn_date date, end_date date, gengou_name varchar(4)); insert into gengou values ('1868-01-01','1912-07-30','明治'), ('1912-07-30','1926-12-25','大正'), ('1926-12-25','1989-01-07','昭和'), ('1989-01-08','9999-12-31','平成');
(2)定義
drop function if exists cng_gengou; -- 存在したら削除 delimiter // -- 終端記号の変更 create function cng_gengou (p_seireki_date date) returns varchar(30) -- -- 西暦→和暦 -- begin declare w_rcnt int default 0; -- 行数チェック declare w_gengou_name varchar(4) default ''; -- 元号名 declare w_bgn_date date; -- 開始日 declare w_nensuu tinyint default 0; -- 和暦年 declare w_rslt varchar(30) default ''; -- 結果 -- select count(*) into w_rcnt from gengou where bgn_date<=p_seireki_date and end_date>=p_seireki_date; if w_rcnt>1 then set w_rslt='2行以上存在するため、未サポート'; else select gengou_name,bgn_date into w_gengou_name,w_bgn_date from gengou where bgn_date<=p_seireki_date and end_date>=p_seireki_date; if length(w_gengou_name)>0 then set w_nensuu=year(p_seireki_date)-year(w_bgn_date)+1; set w_rslt=concat(w_gengou_name,cast(w_nensuu as char(2))); else set w_rslt='change unsuccessful'; end if; end if; return w_rslt; end; // delimiter ; -- 終端記号を元に戻す
(3)実行
select cng_gengou('2006-07-19');
[編集] プロシジャの例2(DB操作あり、非カーソル操作)
(1)表定義及びデータ
「ファンクションの例2(DB操作あり)」と同じ。
(2)定義
drop procedure if exists cng_gengou; -- 存在したら削除 delimiter // -- 終端記号の変更 create procedure cng_gengou (in p_seireki_date date, out p_rslt varchar(30)) -- -- 西暦→和暦 -- begin declare w_rcnt int default 0; -- 行数チェック declare w_gengou_name varchar(4) default ''; -- 元号名 declare w_bgn_date date; -- 開始日 declare w_nensuu tinyint default 0; -- 和暦年 -- select count(*) into w_rcnt from gengou where bgn_date<=p_seireki_date and end_date>=p_seireki_date; if w_rcnt>1 then set p_rslt='2行以上存在するため、未サポート'; else select gengou_name,bgn_date into w_gengou_name,w_bgn_date from gengou where bgn_date<=p_seireki_date and end_date>=p_seireki_date; if length(w_gengou_name)>0 then set w_nensuu=year(p_seireki_date)-year(w_bgn_date)+1; set p_rslt=concat(w_gengou_name,cast(w_nensuu as char(2))); else set p_rslt='change unsuccessful'; end if; end if; end; // delimiter ; -- 終端記号を元に戻す
(3)実行
call cng_gengou('2006-07-19',@rslt); select @rslt;
[編集] プロシジャの例3(DB操作あり、カーソル操作)
(1)表定義及びデータ
「ファンクションの例2(DB操作あり)」と同じ。
(2)定義
drop procedure if exists cng_gengou; -- 存在したら削除 delimiter // -- 終端記号の変更 create procedure cng_gengou (in p_seireki_date date, out p_rslt varchar(30)) -- -- 西暦→和暦 -- begin declare w_rcnt int default 0; -- 行数チェック declare w_gengou_name varchar(4) default ''; -- 元号名 declare w_bgn_date date; -- 開始日 declare w_nensuu tinyint default 0; -- 和暦年 declare eod tinyint; -- カーソル宣言 declare cr1 cursor for select gengou_name,bgn_date from gengou where bgn_date<=p_seireki_date and end_date>=p_seireki_date; -- 例外宣言 declare continue handler for not found set eod=1; set eod=0; open cr1; fetch cr1 into w_gengou_name,w_bgn_date; while eod=0 do set w_rcnt=w_rcnt+1; if w_rcnt>1 then set p_rslt='2行以上存在するため、未サポート'; else if length(w_gengou_name)>0 then set w_nensuu=year(p_seireki_date)-year(w_bgn_date)+1; set p_rslt=concat(w_gengou_name,cast(w_nensuu as char(2))); else set p_rslt='change unsuccessful'; end if; end if; fetch cr1 into w_gengou_name,w_bgn_date; end while; close cr1; end; // delimiter ; -- 終端記号を元に戻す
(3)実行
call cng_gengou('2006-08-10',@rslt); select @rslt;
[編集] PostgreSQLのストアドプロシジャ
[編集] 概要
PostgreSQLのストアドプロシジャは、独自構文で実装されている。このうち、PL/pgSQLでの作成例を、MySQLと同等の例で示す。
[編集] ファンクションの例1(DB操作なし)
(1)定義
create or replace function DecToNshin (dec_num int, n_shin int) returns varchar(32) as $$ -- -- 10進数→n進数 -- declare ltr char(36) default '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; w_dec_num int; amari int; w_DecToNshin varchar(32); begin w_DecToNshin:=''; w_dec_num:=dec_num; while w_dec_num>=n_shin loop amari:=w_dec_num%n_shin; -- 剰余 w_DecToNshin:=substr(ltr,amari+1,1)||w_DecToNshin; w_dec_num:=w_dec_num/n_shin; end loop; w_DecToNshin:=substr(ltr,w_dec_num+1,1)||w_DecToNshin; return w_DecToNshin; end; $$ language 'plpgsql' ;
(2)実行
select DecToNshin(100,16);
(3)削除
仮に削除する場合も、引数の宣言が必要である。
drop function DecToNshin (dec_num int, n_shin int);
[編集] ファンクションの例2(DB操作あり、非カーソル操作)
(1)表定義及びデータ
create table gengou (bgn_date date, end_date date, gengou_name varchar(4)); insert into gengou values('1868-01-01','1912-07-30','明治'); insert into gengou values('1912-07-30','1926-12-25','大正'); insert into gengou values('1926-12-25','1989-01-07','昭和'); insert into gengou values('1989-01-08','9999-12-31','平成');
(2)定義
create or replace function cng_gengou (p_seireki_date date) returns varchar(30) as $$ -- -- 西暦→和暦 -- declare w_rcnt int default 0; -- 行数チェック w_gengou_name varchar(4) default ''; -- 元号名 w_bgn_date date; -- 開始日 w_nensuu smallint default 0; -- 和暦年 w_rslt varchar(30) default ''; -- 結果 begin -- select into w_rcnt -- 標準SQLと語順が異なっている count(*) from gengou where bgn_date<=p_seireki_date and end_date>=p_seireki_date; if w_rcnt>1 then w_rslt:='2行以上存在するため、未サポート'; else select into w_gengou_name,w_bgn_date -- 標準SQLと語順が異なっている gengou_name,bgn_date from gengou where bgn_date<=p_seireki_date and end_date>=p_seireki_date; if length(w_gengou_name)>0 then w_nensuu := extract(year from p_seireki_date) - extract(year from w_bgn_date) + 1; w_rslt:=w_gengou_name||to_char(w_nensuu,'99'); else w_rslt:='change unsuccessful'; end if; end if; return w_rslt; end; $$ language 'plpgsql' ;
(3)実行
select cng_gengou('2006-07-19');
[編集] ファンクションの例3(DB操作あり、カーソル操作)
(1)表定義及びデータ
「ファンクションの例2(DB操作あり、非カーソル操作)」と同じ
(2)定義
create or replace function cng_gengou (p_seireki_date date) returns varchar(30) as $$ -- -- 西暦→和暦 -- declare w_rcnt int default 0; -- 行数チェック w_nensuu smallint default 0; -- 和暦年 w_rslt varchar(30) default ''; -- 結果 eod smallint; cr1 cursor for select gengou_name,bgn_date from gengou where bgn_date<=p_seireki_date and end_date>=p_seireki_date; rec record; begin eod:=0; open cr1; fetch cr1 into rec; while eod=0 loop if not found then raise log 'not fount,rcnt = %',w_rcnt; eod:=1; else w_rcnt:=w_rcnt+1; raise log 'rcnt = %',w_rcnt; if w_rcnt>1 then w_rslt:='2行以上存在するため、未サポート'; else if length(rec.gengou_name)>0 then w_nensuu := extract(year from p_seireki_date) - extract(year from rec.bgn_date) + 1; w_rslt:=rec.gengou_name||to_char(w_nensuu,'99'); else w_rslt:='change unsuccessful'; end if; end if; end if; fetch cr1 into rec; end loop; close cr1; return w_rslt; end; $$ language 'plpgsql' ;
(3)実行
select cng_gengou('2006-08-10');
[編集] SQL Serverのストアドプロシジャ
[編集] 概要
SQL Serverでは、Transact-SQLという独自構文で実装されている。
[編集] ファンクションの例1(DB操作なし)
(1)定義
create function dbo.DecToNshin (@dec_num int, @n_shin smallint) returns varchar(32) -- -- 10進数→n進数 -- begin declare @ltr char(36) declare @w_dec_num int declare @amari int declare @w_DecToNshin varchar(32) set @ltr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' set @w_DecToNshin='' set @w_dec_num=@dec_num while @w_dec_num>=@n_shin begin set @amari=@w_dec_num%@n_shin -- 剰余 set @w_DecToNshin=substring(@ltr,@amari+1,1)+@w_DecToNshin -- +:文字連結 set @w_dec_num=@w_dec_num/@n_shin end set @w_DecToNshin=substring(@ltr,@w_dec_num+1,1)+@w_DecToNshin return @w_DecToNshin end
(2)実行
select dbo.DecToNshin(100,16)
[編集] プロシジャの例1(DB操作なし)
(1)定義
if exists (select * from sysobjects where id = object_id('dbo.DecToNshin') and sysstat & 0xf = 4) drop procedure dbo.DecToNshin GO create procedure dbo.DecToNshin @dec_num int, @n_shin smallint, @w_DecToNshin varchar(32) output as -- -- 10進数→n進数 -- declare @ltr char(36) declare @w_dec_num int declare @amari int set @ltr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' set @w_DecToNshin='' set @w_dec_num=@dec_num while @w_dec_num>=@n_shin begin set @amari=@w_dec_num%@n_shin -- 剰余 set @w_DecToNshin=substring(@ltr,@amari+1,1)+@w_DecToNshin -- +:文字連結 set @w_dec_num=@w_dec_num/@n_shin end set @w_DecToNshin=substring(@ltr,@w_dec_num+1,1)+@w_DecToNshin
(2)実行
declare @out varchar(32) exec dbo.DecToNshin @dec_num=100,@n_shin=16,@w_DecToNshin=@out output select @out
[編集] ファンクションの例2(DB操作あり)
(1)表定義及びデータ
create table gengou (bgn_date datetime, end_date datetime, gengou_name varchar(6)) insert into gengou values('1868-01-01','1912-07-30','明治') insert into gengou values('1912-07-30','1926-12-25','大正') insert into gengou values('1926-12-25','1989-01-07','昭和') insert into gengou values('1989-01-08','9999-12-31','平成')
(2)定義
create function dbo.cng_gengou (@p_seireki_date datetime) returns varchar(60) -- -- 西暦→和暦 -- begin declare @w_rcnt int -- 行数チェック declare @w_gengou_name varchar(6) -- 元号名 declare @w_bgn_date datetime -- 開始日 declare @w_nensuu smallint -- 和暦年 declare @w_rslt varchar(60) -- 結果 -- カーソル宣言 declare cr1 cursor for select gengou_name,bgn_date from gengou where bgn_date<=@p_seireki_date and end_date>=@p_seireki_date -- 初期値 set @w_rcnt=0 set @w_gengou_name='' set @w_nensuu=0 set @w_rslt='' -- カーソルオープン open cr1 -- 1行取り出し fetch cr1 into @w_gengou_name,@w_bgn_date while @@fetch_status=0 begin set @w_rcnt=@w_rcnt+1 if @w_rcnt>1 -- thenは書かない set @w_rslt='2行以上存在するため、未サポート' else begin if len(@w_gengou_name)>0 -- thenは書かない begin set @w_nensuu=year(@p_seireki_date)-year(@w_bgn_date)+1 set @w_rslt=@w_gengou_name+cast(@w_nensuu as char(2)) end else set @w_rslt='change unsuccessful' end -- 1行取り出し fetch cr1 into @w_gengou_name,@w_bgn_date end -- カーソルクローズ close cr1 return @w_rslt end
(3)実行
select dbo.cng_gengou('2006-07-19')
[編集] プロシジャの例2(DB操作あり、非カーソル操作)
(1)表定義及びデータ
「ファンクションの例2(DB操作あり)」と同じ。
(2)定義
if exists (select * from sysobjects where id = object_id('dbo.cng_gengou') and sysstat & 0xf = 4) drop procedure dbo.cng_gengou GO create procedure dbo.cng_gengou @p_seireki_date datetime, @w_rslt varchar(60) output as -- -- 西暦→和暦 -- declare @w_rcnt int -- 行数チェック declare @w_gengou_name varchar(6) -- 元号名 declare @w_bgn_date datetime -- 開始日 declare @w_nensuu smallint -- 和暦年 -- 初期値 set @w_gengou_name='' set @w_nensuu=0 set @w_rslt='' select @w_gengou_name=gengou_name,@w_bgn_date=bgn_date from gengou where bgn_date<=@p_seireki_date and end_date>=@p_seireki_date if @@rowcount>1 -- thenは書かない set @w_rslt='2行以上存在するため、未サポート' else begin if len(@w_gengou_name)>0 -- thenは書かない begin set @w_nensuu=year(@p_seireki_date)-year(@w_bgn_date)+1 set @w_rslt=@w_gengou_name+cast(@w_nensuu as char(2)) end else set @w_rslt='change unsuccessful' end
(3)実行
declare @out varchar(60) exec dbo.cng_gengou @p_seireki_date='2006-08-10',@w_rslt=@out output select @out
[編集] プロシジャの例3(DB操作あり、カーソル操作)
(1)表定義及びデータ
「ファンクションの例2(DB操作あり)」と同じ。
(2)定義
if exists (select * from sysobjects where id = object_id('dbo.cng_gengou') and sysstat & 0xf = 4) drop procedure dbo.cng_gengou GO create procedure dbo.cng_gengou @p_seireki_date datetime, @w_rslt varchar(60) output as -- -- 西暦→和暦 -- declare @w_rcnt int -- 行数チェック declare @w_gengou_name varchar(6) -- 元号名 declare @w_bgn_date datetime -- 開始日 declare @w_nensuu smallint -- 和暦年 -- カーソル宣言 declare cr1 cursor for select gengou_name,bgn_date from gengou where bgn_date<=@p_seireki_date and end_date>=@p_seireki_date -- 初期値 set @w_rcnt=0 set @w_gengou_name='' set @w_nensuu=0 set @w_rslt='' -- カーソルオープン open cr1 -- 1行取り出し fetch cr1 into @w_gengou_name,@w_bgn_date while @@fetch_status=0 begin set @w_rcnt=@w_rcnt+1 if @w_rcnt>1 -- thenは書かない set @w_rslt='2行以上存在するため、未サポート' else begin if len(@w_gengou_name)>0 -- thenは書かない begin set @w_nensuu=year(@p_seireki_date)-year(@w_bgn_date)+1 set @w_rslt=@w_gengou_name+cast(@w_nensuu as char(2)) end else set @w_rslt='change unsuccessful' end -- 1行取り出し fetch cr1 into @w_gengou_name,@w_bgn_date end -- カーソルクローズ close cr1
(3)実行
deallocate cr1 declare @out varchar(60) exec dbo.cng_gengou @p_seireki_date='2006-07-19',@w_rslt=@out output select @out
[編集] ORACLEのストアドプロシジャ
[編集] 概要
Oracleのストアドプロシージャは、PL/SQLという独自構文で実装されている。
[編集] ファンクションの例1(DB操作なし)
(1)定義
-- 10進数→n進数 CREATE OR REPLACE FUNCTION DecToNshin( dec_num IN NUMBER, n_shin IN NUMBER) RETURN VARCHAR2(32) IS ltr CHAR(36); w_dec_num NUMBER; amari NUMBER; w_DecToNshin VARCHAR2(32); BEGIN ltr := '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; w_DecToNshin := ''; w_dec_num := dec_num; WHILE w_dec_num >= n_shin LOOP amari := mod(w_dec_num, n_shin); -- 剰余 w_DecToNshin := substr(ltr, amari+1, 1) || w_DecToNshin; w_dec_num := w_dec_num / n_shin; END LOOP; w_DecToNshin := substr(ltr, w_dec_num+1, 1) || w_DecToNshin; RETURN w_DecToNshin; END; /
(2)実行
SELECT DecToNshin(100, 16);
[編集] 関連項目
- SQL/PSM
- PL/pgSQL
- PL/SQL
- Transact-SQL