web-dev-qa-db-ja.com

SQLストアドプロシージャを記述する際のベストプラクティスは何ですか

SQLストアドプロシージャは非常に興味深く、有用であることがわかりました。私はストアドプロシージャを作成しましたが、あらゆる要件に対応できるように巧妙に作成された、パフォーマンスが調整された簡潔なSPを作成したいと思います。ストアドプロシージャを作成するときに、初心者から上級者向けの段階にどのように移動しますか?

更新:私の質問はより具体的である必要があるというコメントから見つかりました。誰もが彼らの袖の上にいくつかのトリックを持っています、そして私は彼らが他のものと区別し、そしてより重要なことに、彼らをコードで使用するSPのためのそのようなトリックと実践を期待していましたストアドプロシージャの作成と操作の生産性。

30
blntechie

これが私のストアドプロシージャのエラー処理ガイドラインです。

  • パフォーマンスを向上させるために、完全修飾名を使用して各ストアドプロシージャを呼び出します。これは、サーバー名、データベース名、スキーマ(所有者)名、およびプロシージャ名です。
  • 各ストアドプロシージャを作成するスクリプトで、プロシージャの実行を許可するロール(パブリックなど)を明示的に指定します。
  • ハードコードされたエラーメッセージではなく、sysmessage、sp_addmessage、およびプレースホルダーを使用します。
  • Sp_addmessageとsysmessagesを使用する場合は、常に50001以上のエラーメッセージ番号を使用してください。
  • RAISERRORでは、警告メッセージの重大度レベルを常に10以下にしてください。
  • RAISERRORでは、エラーメッセージに対して常に11〜16の重大度レベルを指定します。
  • RAISERRORを使用しても、トリガーコンテキストであっても、進行中のバッチが常に中止されるわけではありません。
  • @@ error を使用または問い合わせる前にローカル変数に保存します。
  • @@ rowcountを使用または問い合わせる前にローカル変数に保存します。
  • ストアドプロシージャの場合は、戻り値を使用して成功/失敗のみを示し、その他/追加の情報は示しません。
  • ストアドプロシージャの戻り値は、成功を示すには0、失敗を示すには0以外に設定する必要があります。
  • ANSI_WARNINGSをONに設定します。これにより、集計割り当て、および文字またはバイナリ列の最大長を超える割り当てでnull値が検出されます。
  • さまざまな理由から、NOCOUNT ONを設定します。
  • XACT_ABORT ONまたはOFF が必要かどうかを慎重に検討してください。どちらに進んでも、首尾一貫してください。
  • 最初のエラーで終了-これはKISSモデルを実装します。
  • ストアドプロシージャを実行するときは、必ず@@ errorと戻り値の両方を確認してください。例えば:

    EXEC @err = AnyStoredProc @value
    SET  @save_error = @@error
    -- NULLIF says that if @err is 0, this is the same as null
    -- COALESCE returns the first non-null value in its arguments
    SELECT @err = COALESCE( NULLIF(@err, 0), @save_error )
    IF @err <> 0 BEGIN 
        -- Because stored proc may have started a tran it didn't commit
        ROLLBACK TRANSACTION 
        RETURN @err 
    END
    
  • エラーが発生するローカルストアドプロシージャを実行する場合は、プロシージャがコミットまたはロールバックしなかったトランザクションを開始した可能性があるため、ロールバックを実行します。
  • トランザクションを開始していないからといって、アクティブなトランザクションがないと思い込まないでください。呼び出し側がトランザクションを開始した可能性があります。
  • 理想的には、呼び出し元によって開始されたトランザクションでロールバックを行わないようにしてください。@@ trancountを確認してください。
  • ただし、トリガーでは、呼び出し元がアクティブなトランザクションを開始したかどうかがわからないため、常にロールバックを実行します(@@ trancountは常に> = 1であるため)。
  • 次のステートメントの後は常に@@ errorを保存して確認してください。

    INSERT, DELETE, UPDATE
    SELECT INTO
    Invocation of stored procedures
    invocation of dynamic SQL
    COMMIT TRANSACTION
    DECLARE and OPEN CURSOR
    FETCH from cursor
    WRITETEXT and UPDATETEXT
    
  • プロセスグローバルカーソル(デフォルト)でDECLARE CURSORが失敗した場合は、ステートメントを発行してカーソルの割り当てを解除します。
  • UDFのエラーに注意してください。 UDFでエラーが発生すると、関数の実行はすぐに中止され、UDFを呼び出したクエリも中止されますが、@@ errorは0です!このような状況では、SET XACT_ABORT ONを指定して実行することをお勧めします。
  • 動的SQLを使用する場合、@@ errorは最後に実行されたコマンドのステータスしか保持しないため、各バッチで単一のSELECTのみを使用するようにしてください。動的SQLのバッチからの最も可能性の高いエラーは構文エラーであり、これらはSET XACT_ABORT ONでは処理されません。
40
RoadWarrior

私が常に使用しようとする唯一のトリックは、次のとおりです。常に上部近くのコメントに使用例を含めてください。これは、SPのテストにも役立ちます。私は最も一般的な例を含めるのが好きです-それはサーバーのすぐそこに保存されているので、お気に入りの呼び出しでSQLプロンプトや個別の.sqlファイルを必要とすることもありません(これは、 sp_whoは、ブロックまたはその他のものを出力し、一連のパラメーターを取ります)。

何かのようなもの:

/*
    Usage:
    EXEC usp_ThisProc @Param1 = 1, @Param2 = 2
*/

次に、SPをテストまたは実行するには、スクリプトでそのセクションを強調表示して実行するだけです。

18
Cade Roux
  1. 常にSET NOCOUNT ONを使用します
  2. 複数の挿入/更新/削除を実行する場合は、トランザクションを使用してください。
  3. プロシージャに「sp_」という名前を付けないでください。 SQL Serverは最初にマスターデータベースを検索しますが、検索しません。次にデータベースを検索します。プロシージャに別の名前を付けると、SQL Serverは最初にデータベースを調べます。

悪い:

SET NOCOUNT ON
BEGIN TRAN
  INSERT...
  UPDATE...
COMMIT

より良いですが、乱雑に見え、コーディングするのに大きな苦痛があります:

SET NOCOUNT ON
BEGIN TRAN
  INSERT...
  IF @ErrorVar <> 0
  BEGIN
      RAISERROR(N'Message', 16, 1)
      GOTO QuitWithRollback
  END

  UPDATE...
  IF @ErrorVar <> 0
  BEGIN
      RAISERROR(N'Message', 16, 1)
      GOTO QuitWithRollback
  END

  EXECUTE @ReturnCode = some_proc @some_param = 123
  IF (@@ERROR <> 0 OR @ReturnCode <> 0)
       GOTO QuitWithRollback 
COMMIT
GOTO   EndSave              
QuitWithRollback:
    IF (@@TRANCOUNT > 0)
        ROLLBACK TRANSACTION 
EndSave:

良い:

SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRY
    BEGIN TRAN
    INSERT...
    UPDATE...
    COMMIT
END TRY
BEGIN CATCH
    IF (XACT_STATE()) <> 0
        ROLLBACK
END CATCH

ベスト:

SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRAN
    INSERT...
    UPDATE...
COMMIT

では、「最善の」ソリューションのエラー処理はどこにあるのでしょうか。何も必要ありません。 SET XACT_ABORT ONを参照してください。これは、エラーが発生した場合に自動ロールバックを実行することを意味します。コードはより簡潔で読みやすく、書きやすく、バグが少ないです。 SQL Serverがこれを行うので、エラー状態を見逃す可能性がないため、バグが少なくなります。

11
Simon Hughes

これは非常に一般的な質問ですが、ここではいくつかのアドバイスを示します。

  • ストアドプロシージャに一貫した名前を付けます。多くの場合、ストアドプロシージャであることを識別するためにプレフィックスを使用しますが、(とにかくSQL Serverで)マスターデータベースに指定されているため、プレフィックスとして 'sp_'を使用しないでください。
  • 可能な戻り値の数を減らすため、NOCOUNTをオンに設定します
  • 多くの場合、セットベースのクエリはカーソルよりもパフォーマンスが優れています。 この質問 これについて詳しく説明します。
  • ストアドプロシージャの変数を宣言する場合は、他の種類のプログラミングと同じように、または必要な適切な命名規則を使用してください。
  • 完全修飾名を使用してSPを呼び出し、どのSPを呼び出す必要があるか)を取り除き、SQL Serverのパフォーマンスを向上させます。これにより、SP問題です。

もちろん、それだけではありません。詳細は次のとおりです。 SQL Serverストアドプロシージャの最適化のヒント

11
AR.

SQL Serverでは、プロシージャが存在する場合はそれを削除するステートメントを常に挿入しているので、開発中にプロシージャを簡単に再作成できます。何かのようなもの:

 IF EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'usp ')AND type in(N'P'、N'PC '))
 DROP PROCEDURE usp 
3
Scott

これは、ストアドプロシージャで何をしているのかに大きく依存します。ただし、1つのプロシージャで複数の挿入/更新または削除を行う場合は、トランザクションを使用することをお勧めします。このようにして、1つの部分に障害が発生した場合、他の部分はロールバックされ、データベースは一貫した状態になります。

データベースに書き込むとき(つまり、選択以外のアクションを実行するストアドプロシージャを使用するとき)に考慮すべき最も重要な2つのことは、データの整合性とパフォーマンスです。データの整合性がないと、ゴミが含まれていて役に立たないデータベースしかありません。 performacneがないと、ユーザー(クライアントの外部にいる場合)や不幸なユーザー(製品の使用が義務付けられている場合、通常は他の場所に行く選択肢のない内部ユーザー)がなくなります。どちらもあなたのキャリアに適していません。そのため、ストアドプロシージャを作成する場合は、まずデータがデータベースに正しく入力され、アクションの一部に問題がある場合は失敗することを確認してください。

最終結果が正しいことを確認するために、必要に応じてチェックをプロシージャに書き込みます。私はETLスペシャリストであり、データをテーブルにインポートする前に、データをクリーンアップして正規化するように常にprocを作成しています。ユーザーインターフェイスから何かを実行している場合、これはprocで行うことはそれほど重要ではないかもしれませんが、データを挿入するためにデータが適切であることを確認するためにprocを実行する前にユーザーインターフェースでチェックを行う必要があります(確認するためのチェックなど)日付フィールドには実際の日付が含まれ、すべての必須フィールドには値が含まれている、など)

大量のデータをテーブルに格納するプロシージャを作成する場合は、ファイナライズされる前にそれらの結果をテストする方法を用意するのが最善です。データのインポートのためにクライアントやベンダーから入手するジャンクに驚かれることでしょう。すべてのインポートプロシージャをテストフラグで記述します。そうすることで、アクションを実行するのではなく、選択したデータを返すことができるため、影響を受けるものを正確に事前に確認できます。

私は動的SQLのファンではなく、ストアドプロシージャで使用することは好みません。既存のプロシージャで動的SQlに行き詰まっている場合は、SQLを実行するのではなく出力できるようにするデバッグフラグを設定してください。次に、実行する必要がある最も一般的なケースをコメントに入力します。これを行えば、プロシージャをより適切に維持できることがわかります。

一度に1つのレコードでのみ機能する別のストアドプロシージャを再利用するために、カーソルで処理を実行しないでください。悪いことにパフォーマンスの問題を引き起こすコードの再利用。

Caseステートメントまたはifステートメントを使用している場合は、可能なすべてのブランチにヒットするテストを行っていることを確認してください。テストしないものは失敗するものです。

2
HLGEM

これは、詳細情報がなくても直接回答できる質問ではありませんが、いくつかの一般的な経験則が実際に適用されます。

ストアドプロシージャは、単に格納されているT-SQLクエリです。したがって、T-SQLおよびさまざまな関数と構文に慣れることが、実際に行う必要があることです。さらに、パフォーマンスの観点から見ると、クエリと基になるデータ構造が適切なパフォーマンスを可能にする方法で一致することを確認する必要があります。 IE、インデックス、関係、制約などが必要な場所に実装されていることを確認してください。

パフォーマンスチューニングツールの使用方法の理解、実行プランの機能の理解、およびその性質から、「次のレベル」に到達する方法

1
Mitchel Sellers

SQL Server 2008では、TRY ... CATCH構文を使用します。これをT-SQLストアドプロシージャ内で使用して、@@ ERRORをチェックすることで、SQL Serverの以前のバージョンで利用できたよりも優れたメカニズムを例外処理に提供できます(多くの場合、各SQLステートメントの後のGOTOステートメントの使用)。

         BEGIN TRY
             one_or_more_sql_statements
         END TRY
         BEGIN CATCH
             one_or_more_sql_statements
         END CATCH

CATCHブロック内では、次のエラー関数を使用して、CATCHブロックを呼び出したエラーに関する情報を取得できます。

         ERROR_NUMBER()
         ERROR_MESSAGE()
         ERROR_SEVERITY()
         ERROR_STATE()
         ERROR_LINE()
         ERROR_PROCEDURE()

実行される各ステートメントによってリセットされる@@ errorとは異なり、エラー関数によって取得されるエラー情報は、TRY ... CATCHステートメントのCATCHブロックのスコープ内のどこでも一定のままです。これらの関数を使用すると、エラー処理を単一のプロシージャにモジュール化できるため、すべてのCATCHブロックでエラー処理コードを繰り返す必要がありません。

1
nk2

以下は、いくつかのベストプラクティスです。

  1. _spプレフィックスを使用しない
  2. SET NOCOUNT ONステートメントを含める
  3. 一時テーブルを使用しないようにしてください
  4. Select * fromの使用は避けてください
  5. カーソルの使用は避けてください
  6. 適切なインデックスを使用する
  7. 適切なエラー処理

詳細とT-SQLコードサンプルについては、この投稿を確認してください

0
nagnath

SQL ServerにマルチレベルのROLLBACKがないことを証明するコードと、トランザクションの処理方法を次に示します。


BEGIN TRAN;

    SELECT @@TRANCOUNT AS after_1_begin;

BEGIN TRAN;

    SELECT @@TRANCOUNT AS after_2_begin;

COMMIT TRAN;

    SELECT @@TRANCOUNT AS after_1_commit;

BEGIN TRANSACTION;

    SELECT @@TRANCOUNT AS after_3_begin;

ROLLBACK TRAN;

    SELECT @@TRANCOUNT AS after_rollback;
0
darlove

基本的なもの:

エラー処理ポリシーを設定し、すべてのSQLステートメントでエラーをトラップします。
ストアドプロシージャのソースコード管理を使用するためのポリシーを決定します。
コメント付きのヘッダーをユーザー、日付/時刻、およびspの目的とともに含めます。
正常に実行された場合は明示的に0(成功)を返し、それ以外の場合は何かを返します。
重要な手順については、テストケース(1つまたは複数)と予想される結果の説明を含めます。
パフォーマンステストの習慣を身につけましょう。テキストの場合は、少なくとも実行時間を記録します。
明示的なトランザクションを理解して使用します。
SPからSPを呼び出すことはほとんどありません。再利用性は、SQLの別の球技です。

0
dkretz