この記事では、SQLカーソルと、それらを特別な目的で使用する方法について説明します。 SQLカーソルの重要性とその欠点を強調しています。
データベースプログラミングでSQLカーソルを使用する場合は必ずしもそうとは限りませんが、SQLカーソルの概念を理解し、その使用方法を学ぶことは、T-SQLプログラミングで例外的なタスクを実行する方法を理解するのに大いに役立ちます。
SQLカーソルの概要
SQLカーソルに慣れていない場合は、SQLカーソルの基本をいくつか見ていきましょう。
簡単な定義
SQLカーソルを使用すると、一度に1行ずつデータにアクセスできるため、結果セットをより(行ごとに)制御できます。
Microsoftの定義
Microsoftのドキュメントによると、Microsoft SQL Serverステートメントは完全な結果セットを生成しますが、結果が一度に1行ずつ処理されるのが最適な場合があります。結果セットにカーソルを開くと、一度に1行ずつ結果セットを処理できます。
T-SQLと結果セット
SQLカーソルの単純な定義とMicrosoftの定義の両方で結果セットが言及されているので、データベースプログラミングのコンテキストで結果セットが正確に何であるかを理解してみましょう。次のように、サンプルデータベースUniversityV3にStudentsテーブルをすばやく作成して入力します。
CREATE TABLE [dbo].[Student] ( [StudentId] INT IDENTITY (1, 1) NOT NULL, [Name] VARCHAR (30) NULL, [Course] VARCHAR (30) NULL, [Marks] INT NULL, [ExamDate] DATETIME2 (7) NULL, CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED ([StudentId] ASC) ); -- (5) Populate Student table SET IDENTITY_INSERT [dbo].[Student] ON INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (1, N'Asif', N'Database Management System', 80, N'2016-01-01 00:00:00') INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (2, N'Peter', N'Database Management System', 85, N'2016-01-01 00:00:00') INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (3, N'Sam', N'Database Management System', 85, N'2016-01-01 00:00:00') INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (4, N'Adil', N'Database Management System', 85, N'2016-01-01 00:00:00') INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (5, N'Naveed', N'Database Management System', 90, N'2016-01-01 00:00:00') SET IDENTITY_INSERT [dbo].[Student] OFF
次に、学生からすべての行を選択します テーブル:
-- View Student table data SELECT [StudentId], [Name], [Course], [Marks], [ExamDate] FROM dbo.Student
これは、学生からすべてのレコードを選択した結果として返される結果セットです。 テーブル。
T-SQLと集合論
T-SQLは、純粋に次の2つの数学的概念に基づいています。
- 集合論
- 述語論理
集合論は、その名前が示すように、集合に関する数学の一分野であり、明確な別個のオブジェクトのコレクションとも呼ばれます。
要するに、集合論では、私たちは個々のアイテムを考えるのと同じように、物や物を全体として考えます。
たとえば、学生は明確に区別できるすべての学生のセットであるため、そのセット内のすべての学生の詳細を取得するのに十分な学生全体を取り上げます(表)。
詳細については、私の記事「SQLでデータを単純なものからスライディングなものに集約する方法」を参照してください。
カーソルと行ベースの操作
T-SQLは主に、テーブルからすべてのレコードを選択したり、テーブルからすべての行を削除したりするなど、セットベースの操作を実行するように設計されています。
つまり、T-SQLは、セットベースの方法でテーブルを操作するように特別に設計されています。つまり、テーブル全体を考え、選択、更新、削除などの操作は、テーブル全体または特定のテーブルに適用されます。基準を満たす行。
ただし、単一の結果セットとしてではなく、行ごとにテーブルにアクセスする必要がある場合があります。これは、カーソルが機能する場合です。
Vaidehi Pandereによると、アプリケーションロジックは、結果セット全体をループする(ループを使用して繰り返す)のと同じように、一度にすべての行を処理するのではなく、一度に1つの行を処理する必要がある場合があります。
SQLカーソルの基本と例
SQLカーソルについて詳しく説明しましょう。
まず、T-SQLでカーソルを使用する方法を学習または確認しましょう(T-SQLでのカーソルの使用に既に精通している人)。
SQLカーソルの使用は、次のように表される5つのステップのプロセスです。
- カーソルを宣言する
- カーソルを開く
- 行を取得
- カーソルを閉じる
- カーソルの割り当てを解除する
ステップ1:カーソルを宣言する
最初のステップは、後で使用できるようにSQLカーソルを宣言することです。
SQLカーソルは次のように宣言できます:
DECLARE Cursor <Cursor_Name> for <SQL statement>
ステップ2:カーソルを開く
宣言後の次のステップは、カーソルを開くことです。これは、次のように表される結果セットをカーソルに入力することを意味します。
Open <Cursor_Name>を開きます
ステップ3:行をフェッチする
カーソルが宣言されて開かれると、次のステップはSQLカーソルから行を1つずつ取得し始め、フェッチ行がSQLカーソルから次に使用可能な行を取得するようにします。
Open <Cursor_Name>から次を取得
ステップ4:カーソルを閉じる
行が1つずつフェッチされ、要件に従って操作されたら、次のステップはSQLカーソルを閉じることです。
SQLカーソルを閉じると、次の3つのタスクが実行されます。
- カーソルが現在保持している結果セットを解放します
- カーソルによる行のカーソルロックを解除します
- 開いているカーソルを閉じます
カーソルを閉じるための簡単な構文は次のとおりです。
Close <Cursor_Name>
ステップ5:カーソルの割り当てを解除する
この点に関する最後のステップは、カーソルの割り当てを解除して、カーソル参照を削除することです。
構文は次のとおりです。
DEALLOCATE <Cursor_Name>
SQLカーソルの互換性
Microsoftのドキュメントによると、SQLカーソルは次のバージョンと互換性があります。
- SQLServer2008以降のバージョン
- AzureSQLデータベース
SQLカーソルの例1:
SQLカーソルの実装に関連する手順を理解したので、SQLカーソルの使用例を見てみましょう。
-- Declare Student cursor example 1 USE UniversityV3 GO DECLARE Student_Cursor CURSOR FOR SELECT StudentId ,[Name] FROM dbo.Student; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor WHILE @@FETCH_STATUS = 0 BEGIN FETCH NEXT FROM Student_Cursor END CLOSE Student_Cursor DEALLOCATE Student_Cursor
出力は次のとおりです。
SQLカーソルの例2:
この例では、2つの変数を使用して、カーソルが行から行に移動するときにカーソルが保持するデータを格納し、変数の値を表示して結果セットを一度に1行ずつ表示できるようにします。
-- Declare Student cursor with variables example 2 USE UniversityV3 GO DECLARE @StudentId INT ,@StudentName VARCHAR(40) -- Declare variables to hold row data held by cursor DECLARE Student_Cursor CURSOR FOR SELECT StudentId ,[Name] FROM dbo.Student; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor INTO @StudentId, @StudentName -- Fetch first row and store it into variables WHILE @@FETCH_STATUS = 0 BEGIN PRINT CONCAT(@StudentId,'--', @StudentName) -- Show variables data FETCH NEXT FROM Student_Cursor -- Get next row data into cursor and store it into variables INTO @StudentId, @StudentName END CLOSE Student_Cursor -- Close cursor locks on the rows DEALLOCATE Student_Cursor -- Release cursor reference
上記のSQLコードの結果は次のとおりです。
次のような単純なSQLスクリプトを使用することで、同じ出力を実現できると主張する人もいるかもしれません。
-- Viewing student id and name without SQL cursor SELECT StudentId,Name FROM dbo.Student order by StudentId
実際、メモリに直接影響するためSQLカーソルの使用は推奨されていませんが、SQLカーソルの使用を必要とするタスクはかなりあります。
重要な注意事項
Vaidehi Pandereによると、カーソルはメモリに常駐するポインタのセットであるため、他の重要なプロセスで使用されるシステムメモリを占有することに注意してください。そのため、正当な理由がない限り、カーソルを介して大きな結果セットをトラバースすることは決して良い考えではありません。
特別な目的でのSQLカーソルの使用
SQLカーソルを使用できるいくつかの特別な目的について説明します。
データベースサーバーのメモリテスト
SQLカーソルはシステムメモリに大きな影響を与えるため、さまざまなストアドプロシージャやアドホックSQLスクリプトによる過剰なメモリ使用量を調査する必要があるシナリオを再現するのに適しています。
これを理解する簡単な方法の1つは、SSMS(SQL Server Management Studio)のツールバーのクライアント統計ボタンをクリックして(またはShift + Alt + Sを押して)、カーソルなしで簡単なクエリを実行することです。
次に、変数を使用してカーソルでクエリを実行します(SQLカーソルの例2):
違いを書き留めてください:
カーソルのないSELECTステートメントの数:1
カーソルのあるSELECTステートメントの数:7
カーソルなしのサーバーラウンドトリップの数:1
カーソルを使用したサーバーのラウンドトリップ数:2
カーソルなしのクライアント処理時間:1
カーソルを使用したクライアントの処理時間:8
カーソルなしの合計実行時間:1
カーソルを使用した合計実行時間:38
カーソルなしのサーバー応答の待機時間:0
カーソルを使用したサーバー応答の待機時間:30
つまり、5行だけを返すカーソルなしでクエリを実行すると、カーソルを使用して同じクエリが6〜7回実行されます。
これで、カーソルを使用してメモリへの影響を再現するのがいかに簡単か想像できますが、これが常に最善の方法であるとは限りません。
一括データベースオブジェクト操作タスク
SQLカーソルが便利な別の領域があります。これは、データベースまたはデータベースオブジェクトに対して一括操作を実行する必要がある場合です。
これを理解するには、まず、コーステーブルを作成し、 UniversityV3に入力する必要があります。 次のようなデータベース:
-- Create Course table CREATE TABLE [dbo].[Course] ( [CourseId] INT IDENTITY (1, 1) NOT NULL, [Name] VARCHAR (30) NOT NULL, [Detail] VARCHAR (200) NULL, CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED ([CourseId] ASC) ); -- Populate Course table SET IDENTITY_INSERT [dbo].[Course] ON INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (1, N'DevOps for Databases', N'This is about DevOps for Databases') INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (2, N'Power BI Fundamentals', N'This is about Power BI Fundamentals') INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (3, N'T-SQL Programming', N'About T-SQL Programming') INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (4, N'Tabular Data Modeling', N'This is about Tabular Data Modeling') INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (5, N'Analysis Services Fundamentals', N'This is about Analysis Services Fundamentals') SET IDENTITY_INSERT [dbo].[Course] OFF
ここで、 UniversityV3内の既存のすべてのテーブルの名前を変更するとします。 OLDテーブルとしてのデータベース。
これには、名前を変更できるように、すべてのテーブルを1つずつカーソルで繰り返す必要があります。
次のコードがその役割を果たします:
-- Declare Student cursor to rename all the tables as old USE UniversityV3 GO DECLARE @TableName VARCHAR(50) -- Existing table name ,@NewTableName VARCHAR(50) -- New table name DECLARE Student_Cursor CURSOR FOR SELECT T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor INTO @TableName WHILE @@FETCH_STATUS = 0 BEGIN SET @[email protected]+'_OLD' -- Add _OLD to exsiting name of the table EXEC sp_rename @TableName,@NewTableName -- Rename table as OLD table FETCH NEXT FROM Student_Cursor -- Get next row data into cursor and store it into variables INTO @TableName END CLOSE Student_Cursor -- Close cursor locks on the rows DEALLOCATE Student_Cursor -- Release cursor reference
おめでとうございます。SQLカーソルを使用して、既存のすべてのテーブルの名前を正常に変更しました。
やるべきこと
SQLカーソルの使用に慣れたので、次のことを試してください。
- カーソルを使用して、サンプルデータベースのすべてのテーブルのインデックスを作成して名前を変更してみてください。
- カーソルを使用して、この記事で名前を変更したテーブルを元の名前に戻してみてください。
- テーブルに多数の行を入力して、カーソルがある場合とない場合のクエリの統計と時間を測定してください。