SQLServerでのカーソルの使い方と書き方!

こんにちは!ヒロポンです!

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でのカーソルの使い方と書き方に入っていきましょう!

カーソルの基本的な流れ

カーソルの基本的な流れは以下の通りです。

  1. カーソル宣言
  2. カーソルOpen
  3. カーソルの1行目を取り出し、上記変数に入れる
  4. 最終行まで処理をして、クローズ

カーソルの具体的な構文

以下のテーブルを例に、具体的な構文を見てみましょう。
テーブル名: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 のカーソルは比較的明示的で重い。

📚 関連記事

この記事が気に入ったら
いいねしてね!

どんどんシェア待ってるぜ!!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次