プログラムを使用すれば、SQLのSELECTステートメントで取得したデータをループして1件ずつ処理することができますが、仕様によっては、データベース側でループ処理を行いたい場合もあります。
例えば、ストアドプロシージャ内でSELECTを使用して取得したデータをループして、そのデータをもとに特定のテーブルにデータを更新したい場合などです。
このような場合にSQLServerでは、FETCHステートメントを使用することができます。
今回は、サーバーカーソルから特定の行を取得してループ処理を行うFETCHを紹介します。
目次
本記事で使用するサンプルテーブル
本記事ではFETCHを使用したSQLのサンプルとして、以下のテーブルを使用します。
テーブルは、会員の個人情報を管理する「会員」テーブルと、会員の連絡先情報を管理する「連絡先」テーブルの2つを作成します。
テーブルの列定義
テーブルの列定義は次のようになります。
列名 | データ型 | NULL許容 | 主キー |
---|---|---|---|
会員番号 | int | No | 〇 |
姓 | nvarchar(50) | No | |
名 | nvarchar(50) | No | |
性別 | varchar(10) | Yes | |
生年月日 | date | Yes |
列名 | データ型 | NULL許容 | 主キー |
---|---|---|---|
会員番号 | int | No | 〇 |
連絡先番号 | tinyint | No | 〇 |
電話番号 | varchar(20) | Yes | |
メールアドレス | varchar(254) | Yes | |
備考 | nvarchar(200) | Yes |
テーブルを作成するCREATE文(SQL)
テーブルを作成するSQLのCREATE文は次のようになります。
1 2 3 4 5 6 7 8 9 |
CREATE TABLE 会員 ( 会員番号 int NOT NULL, 姓 nvarchar(50) NULL, 名 nvarchar(50) NULL, 性別 varchar(10) NULL, 生年月日 date NULL, PRIMARY KEY(会員番号) ); |
1 2 3 4 5 6 7 8 9 |
CREATE TABLE 連絡先 ( 会員番号 int NOT NULL, 連絡先番号 int NOT NULL, 電話番号 varchar(20) NULL, メールアドレス varchar(254) NULL, 備考 nvarchar(200) NULL, PRIMARY KEY(会員番号, 連絡先番号) ); |
テーブルにデータを挿入するINSERT文(SQL)
テーブルにデータを挿入するSQLのINSERT文は次のようになります。
1 2 3 4 5 6 7 8 |
INSERT INTO 会員 (会員番号, 姓, 名, 性別, 生年月日) VALUES (1001, '山田', '太郎', '男', '1999/03/27'), (1002, '佐藤', '花子', '女', '1987/06/23'), (1003, '牛田', 'べこら', '不明', '2001/05/11'), (1004, '馬田', 'ぱから', 'その他', '1994/12/04'), (1005, '池畑', '明宏', '両方', '2005/09/08'); |
FETCHとは
FETCHは、データベースから取得したデータを1件ずつ参照する機能です。
FETCHでは、カーソル(CURSOR)を使用します。
カーソルとは、取得結果からデータを1件ずつ抜き取るための仕組みです。
カーソルは、取得したデータ(表)の位置(行)情報を持っています。
FETCHでは、カーソルを利用してデータを1行ずつループすることで、値を参照することができます。
FETCHステートメントの使用例
ここでは、FETCHステートメントを使用した簡単な例をいくつか紹介します。
単純なフェッチ
次の例では、「会員」テーブルのすべての行に対して単純なカーソルを宣言し、FETCH NEXTを使用して行を順番に移動します。
FETCHステートメントは、DECLARE CURSORステートメントで宣言された列の値を、単一行の結果セットとして返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
-- カーソルを宣言します。 DECLARE カーソル CURSOR FOR SELECT 姓, 名 FROM 会員 ORDER BY 会員番号; -- カーソルを開きます。 OPEN カーソル; -- 最初のフェッチを実行します。(先頭行に移動) FETCH NEXT FROM カーソル; -- @@ FETCH_STATUSをチェックしてフェッチ可能かを判定します。 WHILE @@FETCH_STATUS = 0 BEGIN -- 次のフェッチを実行します。(次の行に移動) FETCH NEXT FROM カーソル; END -- カーソルを閉じます。 CLOSE カーソル; -- カーソルの割り当てを解除します。 DEALLOCATE カーソル; |
FETCHステートメントを使用する際は、まずカーソルを宣言(定義)します。
宣言したカーソルはOPENステートメントで開きます。
カーソルが開けたら、「FETCH NEXT」で行を移動します。
初回の「FETCH NEXT」で、取得したデータ(結果表)の先頭行に移動することができます。
「WHILE @@FETCH_STATUS = 0」でフェッチの状態を確認します。
WHILEループでは、フェッチが可能な間処理を行います。
ループ内で「FETCH NEXT」を実行することで次の行に移動することができます。
カーソルが最終行まで移動したらWHILEループを抜けますので、カーソルを閉じて割り当てを解除します。
上記のSQLをSQL Server Management Studio(SSMS)実行すると、次のような結果になります。
データの中身(列の値)を参照して変数に格納する
次の例では、「会員」テーブルのすべての行に対してカーソルを宣言し、FETCHステートメントで変数に値を格納します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
-- データを格納する変数を宣言(定義)します。 DECLARE @会員番号 int, @姓 nvarchar(50), @名 nvarchar(50); -- カーソルを宣言します。 DECLARE カーソル CURSOR FOR SELECT 会員番号, 姓, 名 FROM 会員 ORDER BY 会員番号; -- カーソルを開きます。 OPEN カーソル; -- 最初のフェッチを実行して変数に値を格納します。 FETCH NEXT FROM カーソル INTO @会員番号, @姓, @名; -- @@ FETCH_STATUSをチェックしてフェッチ可能かを判定します。 WHILE @@FETCH_STATUS = 0 BEGIN -- 変数の情報を出力します。 PRINT '会員番号: ' + CONVERT(NVARCHAR, @会員番号) + '氏名: ' + @姓 + ' ' + @名 -- 次のフェッチを実行して変数に値を格納します。 FETCH NEXT FROM カーソル INTO @会員番号, @姓, @名; END -- カーソルを閉じます。 CLOSE カーソル; -- カーソルの割り当てを解除します。 DEALLOCATE カーソル; |
FETCHで変数に値を格納する際は、INTOを使用します。
INTOの後にSELECTで取得する列の順番に、変数を記述します。
カーソル フェッチ: INTO リストで宣言する変数の数は、選択される列の数と一致させてください。
参照したデータをもとに別のテーブルを更新する
次の例では、FETCHステートメントで値を格納した変数を利用して、別のテーブルを更新(INSERT)します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
-- データを格納する変数を宣言(定義)します。 DECLARE @会員番号 int, @姓 nvarchar(50), @名 nvarchar(50); -- カーソルを宣言します。 DECLARE カーソル CURSOR FOR SELECT 会員番号, 姓, 名 FROM 会員 ORDER BY 会員番号; -- カーソルを開きます。 OPEN カーソル; -- 最初のフェッチを実行して変数に値を格納します。 FETCH NEXT FROM カーソル INTO @会員番号, @姓, @名; -- @@ FETCH_STATUSをチェックしてフェッチ可能かを判定します。 WHILE @@FETCH_STATUS = 0 BEGIN -- 変数を利用して連絡先テーブルにレコードを挿入します。 INSERT INTO 連絡先 ( 会員番号, 連絡先番号, 電話番号, メールアドレス, 備考 ) VALUES ( @会員番号, 1, '登録してください。', '登録してください。', @姓 + ' ' + @名 ) -- 次のフェッチを実行して変数に値を格納します。 FETCH NEXT FROM カーソル INTO @会員番号, @姓, @名; END -- カーソルを閉じます。 CLOSE カーソル; -- カーソルの割り当てを解除します。 DEALLOCATE カーソル; |
FETCHで値を格納した変数は、INSERT、UPDATE、DELETEなどのSQLの実行に使用できます。
これを利用する事で、通常のSQLでは実現できない処理を行うストアドプロシージャなどを作成することできます。
SCROLLカーソルを宣言して様々なFETCHを行う
ここまでの例では、DECLARE カーソル CURSOR FORでカーソルを宣言してきました。
この宣言の仕方の場合には、カーソルは順方向専用(先頭から末尾に進むのみ)のカーソルになります。
取得したデータを上から順番に読み込んでいくフェッチ処理の場合は、順方向専用のカーソルを宣言して、FETCH NEXTでカーソルをすすめていきますが、SQLServerには、カーソルを先頭や末尾に移動したり、指定した位置の行にカーソルを移動するオプションも用意されています。
FETCHオプションにはNEXTの他に
PRIOR
FIRST
LAST
ABSOLUTE
RELATIVE
があります。
「PRIOR」は、現在のカーソルがある行の1つ前の行に移動します。
「FIRST」は、カーソル内の先頭行に移動します。
「LAST」は、カーソル内の最終行移動します。
「ABSOLUTE」は、指定した行数の番号(n行目)の行に移動します。
「RELATIVE」は、現在のカーソルがある行を起点として上または下に指定した行数の分だけ移動します。
以下にFETCHステートメントで使用できるオプションの使用例を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
-- カーソルを宣言します。 DECLARE カーソル SCROLL CURSOR FOR SELECT 姓, 名 FROM 会員 ORDER BY 会員番号; -- カーソルを開きます。 OPEN カーソル; -- カーソルの最後の行をフェッチします。 FETCH LAST FROM カーソル; -- カーソル内の現在の行の直前の行をフェッチします。 FETCH PRIOR FROM カーソル; -- カーソルの2番目の行をフェッチします。 FETCH ABSOLUTE 2 FROM カーソル; -- 現在の行の3行後の行をフェッチします。 FETCH RELATIVE 3 FROM カーソル; -- 現在の行の2行前の行をフェッチします。 FETCH RELATIVE -2 FROM カーソル; -- カーソルを閉じます。 CLOSE カーソル; -- カーソルの割り当てを解除します。 DEALLOCATE カーソル; |
「NEXT」以外のオプションを利用する場合は、カーソルの宣言時にSCROLLを指定してDECLARE カーソル SCROLL CURSOR FORとします。
上記のSQLをSQL Server Management Studio(SSMS)実行すると、次のような結果になります。
「ABSOLUTE」と「RELATIVE」では、数値でカーソル位置を指定します。
「RELATIVE」で0より小さい数値を指定すると上方向(逆方向)にカーソルが移動します。
カーソルの種類(オプション)
カーソルには、SCROLLオプション以外にも
FORWARD_ONLY
FAST_FORWARD
DYNAMIC
などがあります。
カーソルの種類(オプション)をはじめ、SQL ServerのFETCHの詳細については、以下のドキュメントが参考になります。