こんにちは!ヒロポンです!
SQLってWEB系のフレームワークを使っていたりすると、あまり意識することないと思いますが、、、
私は会社でSQLServerを使ってDBを管理しています。
なのでゴリゴリでSQL文書きます。
DBとアプリ側で処理を分ける際に、悩むこともあります。
ですがアプリ側で出来ればSQL処理は書きたくないところ。
そんなときに出会ったのが、SQLServerでのカーソルってやつなんですね!
SQLのカーソルの存在意義って?
💡 カーソルを使うべきタイミングと避けるべき場面については別記事 SQL Server のカーソルを使うべきタイミングと書き方 で判断軸まとめてます。
私なりに理解をしたSQLでのカーソルの存在意義等を書いていきます。
そもそもカーソルとは、DBの行を1行ずつ変数に入れて、任意の処理をして、最後の行まで回すという処理です。
プログラムでいうところの、For eachににていますね!
このカーソルのおいしさ!存在意義なのですが、1行ずつInsertしたい時に使うのかなーーと思います。
具体的には、テーブルAからテーブルB,C,Dを作成する際などに使うと存在意義があるのかなーーと思います。
現在私が思うカーソルの存在意義はInsert文くらいです。。。それ以外にご存じの方は教えていただけますでしょうか。。。。
SQLServerでのカーソルの使い方と書き方
では早速本編!SQLServerでのカーソルの使い方と書き方に入っていきましょう!
カーソルの基本的な流れ
カーソルの基本的な流れは以下の通りです。
- カーソル宣言
- カーソルOpen
- カーソルの1行目を取り出し、上記変数に入れる
- 最終行まで処理をして、クローズ
カーソルの具体的な構文
以下のテーブルを例に、具体的な構文を見てみましょう。
テーブル名:customer
| CustomerName | age | address |
| 鈴木 | 20 | 東京 |
| 斎藤 | 30 | 神奈川 |
| 田中 | 25 | 東京 |
| 佐藤 | 26 | 千葉 |
カーソルで使う変数の宣言
declare
@CurName nvarchar(20),
@CurAge int,
@address nvarchar(20)
カーソルの宣言
declare curデータ cursor local for
select CustomerName,age,address
from customer
カーソルのオープン
open curデータ
カーソルの値を変数に入れる
fetch next from curデータ
into @CurName,@CurAge,@address
最終行までループ
while(@@fetch_status = 0)
BEGIN
-- ここに特定の処理を書く
-- 処理が終わったら変数の入れ替え
fetch next from curデータ
into @CurName,@CurAge,@address
END
カーソルを閉じる
close curデータ
deallocate curデータ
以上が基本的なカーソルの書き方となります。
ですが、実際はカーソルの途中でエラーになった時のことなどを考え、トランザクションも同時に考えたほうがいいのではないかと思います。
その場合の書き方は以下のようになります。
begin try
begin transaction
declare
@CurName nvarchar(20),
@CurAge int,
@address nvarchar(20)
declare curデータ cursor local for
select CustomerName,age,address
from customer
open curデータ
fetch next from curデータ
into @CurName,@CurAge,@address
while(@@fetch_status = 0)
BEGIN
-- ここに特定の処理を書く
-- 処理が終わったら変数の入れ替え
fetch next from curデータ
into @CurName,@CurAge,@address
END
close curデータ
deallocate curデータ
commit transaction
end try
begin catch
rollback transaction
end catch
まとめ
SQLのカーソルはデータを1行ずつ取り出したいときに使うもので、例えばデータを1行ずつ条件かけてInsertする際に使ったりするといいのではないでしょうか。
とは言ってみたものの、大体はカーソル無しで実装できることが多いため、今後どのような場面でカーソルを使うのか分かればここに書いていきたいと思います!
💡 補足: 業務系の現場でよくハマるパターン
俺も SQL Server のカーソル、業務でハマってきたところを3つ並べておきます。
① カーソル使うべきじゃない場面で使ってパフォーマンス死亡
「1行ずつ処理する」をカーソルで書くと、 SET ベース処理(UPDATE / INSERT SELECT)の100倍遅い。集合演算でできることは絶対カーソル使わない。詳しくは カーソル判断軸 で書いてます。
② DEALLOCATE 忘れで接続リーク
カーソル CLOSE しても DEALLOCATE しないとリソース解放されない。 batch 処理だと数千カーソル溜まって接続不能になる事故。 TRY-CATCH の FINALLY 相当でセットで叩く癖をつける。
③ カーソル内 UPDATE で同じ行を二重処理
カーソル走査中に対象テーブルを UPDATE すると、 isolation level によっては同じ行が再度ヒットして無限ループ。 STATIC / FAST_FORWARD カーソルで回避。
❓ よくある質問
Q1. カーソルの代替手段は?
A. WHILE ループ + 一時テーブル、 CTE (WITH 句)、 ウィンドウ関数(ROW_NUMBER / LAG / LEAD)。 SET ベースで書き直せる場合は必ずそっち。
Q2. C# から SQL カーソル呼ぶのと C# 側で DataReader 回すのどっちがいい?
A. C# 側で DataReader 回す方が高速。 SQL カーソルは複雑な集計を SQL 内で完結させる時だけ。データ加工だけなら C# の方が柔軟&速い。 DataReader vs DataAdapter で関連書いてます。
Q3. FAST_FORWARD オプションって何?
A. 読み取り専用+順方向のみのカーソル。最高速。 SQL Server で「ただ走査するだけ」なら必ずこれを付ける。デフォルトの双方向 + 更新可能カーソルは無駄に重い。
Q4. ストアドプロシージャ内のカーソル、デバッグどうする?
A. PRINT 文で進捗出す or 一時テーブルに途中結果書き込んでログ化。 SSMS のデバッガもあるが業務では PRINT デバッグの方が早い。
Q5. SQL Server 以外(Oracle / PostgreSQL)のカーソルとの違いは?
A. 文法レベルでは似てるが、 PL/SQL(Oracle) は暗黙カーソルが多い、 PostgreSQL の REFCURSOR は関数戻り値で扱える等の違いあり。 SQL Server のカーソルは比較的明示的で重い。
📚 関連記事
- SQL Server のカーソルを使うべきタイミングと書き方 — 判断軸まとめ
- SQL Server の DBNull を C# で安全にハンドリングする5つのイディオム — DBNull の取り扱い
- FormとFormの間で値の共有・受け渡し — WinForms との連携
- DataGridViewにDataTable反映 — SQL 結果を DataGridView に出すパターン
- Formの中にFormを表示して切り替え — Master/Detail 表示

コメント