![]() ![]() ![]() ![]() 大量の結果セットをさらに効率的にページングする方法この記事のURLhttp://japan.internet.com/developer/20060725/25.html
著者:Greg Hamilton
海外internet.com発の記事
はじめにクエリ結果を効率的にページングするという問題に対しては、驚くほどのサイクルとハードウェアとブレインウェアが投入されています。先日掲載されたScott Mitchellの記事『SQL Server 2000で大量の結果セットを効率的にページングする方法』では、テーブルからデータの特定の「ページ」を返すストアドプロシージャを考察していました。Scottの方法を調べてみたところ、まだ改善の余地があることが分かりました(本稿はScottの例を基にしているので、Scottの記事を読んでいない方は、そちらを先にお読みください)。 Scottの方法ではテーブル変数を使って、行カウンタとして機能する補助的なIDを生成していました。つまり、ページが要求されるたびに、ページング対象のテーブル内の全データを読み取ってテーブル変数に挿入し、そこで補助的なIDを生成したうえで、目的の範囲内のIDを持つレコードだけを 本稿ではScottの方法を改善する2つの方法を説明します。最初の方法では、Scottと同様にテーブル変数を使用しますが、読み取ってテーブル変数に挿入するレコードの数を減らすために ROWCOUNTでページングを最適化する ページングを最適化するための最初のステップは、テーブル変数にレコードを挿入する前に このストアドプロシージャの例は、4Guysの読者であるDave Griffithsが作成したものです。 CREATE PROCEDURE usp_PagedResults_New_Rowcount ( @startRowIndex int, @maximumRows int ) AS DECLARE @TempItems TABLE ( ID int IDENTITY, EmployeeID int ) DECLARE @maxRow int -- A check can be added to make sure @startRowIndex isn’t > count(1) -- from employees before doing any actual work unless it is guaranteed -- the caller won’t do that SET @maxRow = (@startRowIndex + @maximumRows) - 1 SET ROWCOUNT @maxRow INSERT INTO @TempItems (EmployeeID) SELECT EmployeeID FROM Employees ORDER BY EmployeeID SET ROWCOUNT @maximumRows SELECT e.*, d.[Name] as DepartmentName FROM @TempItems t INNER JOIN Employees e ON e.EmployeeID = t.EmployeeID INNER JOIN Departments d ON d.DepartmentID = e.DepartmentID WHERE ID >= @startRowIndex SET ROWCOUNT 0 GO ご覧のように、このストアドプロシージャでは、最初に SQL Serverのオプティマイザを利用するこのシナリオでは、1つの変数に対して潜在的な値リストを代入すると、そのリストの最後の項目の値が割り当てられるというオプティマイザの「癖」を利用しています。例えば次のSQLスクリプトでは、テーブル変数を作成し、そこに100個のレコード(1〜100)を挿入した後で、テーブル全体のval列の値を2種類のソートを使ってローカル変数に代入しています。 DECLARE @tmp TABLE( val int ) DECLARE @i int, @cnt int, @res int SELECT @i = 0, @cnt = 100 WHILE @i < @cnt BEGIN SELECT @i = @i + 1 INSERT INTO @tmp VALUES(@i) end SELECT @res = val FROM @tmp ORDER BY val ASC SELECT @res [Value], ’ASC Sort’ [Sort] SELECT @res = val FROM @tmp ORDER BY val DESC SELECT @res [Value], ’DESC Sort’ [Sort] これらの Value Sort ----- ---- 100 ASC Sort Value Sort ----- ---- 1 DESC Sort SQL Serverが では、このオプティマイザの癖を、大量の結果セットをページングするという問題にどう応用したらいいのでしょうか。この知識と CREATE PROCEDURE [dbo].[usp_PageResults_NAI] ( @startRowIndex int, @maximumRows int ) AS DECLARE @first_id int, @startRow int -- A check can be added to make sure @startRowIndex isn’t > count(1) -- from employees before doing any actual work unless it is guaranteed -- the caller won’t do that -- Get the first employeeID for our page of records SET ROWCOUNT @startRowIndex SELECT @first_id = employeeID FROM employees ORDER BY employeeid -- Now, set the row count to MaximumRows and get -- all records >= @first_id SET ROWCOUNT @maximumRows SELECT e.*, d.name as DepartmentName FROM employees e INNER JOIN Departments D ON e.DepartmentID = d.DepartmentID WHERE employeeid >= @first_id ORDER BY e.EmployeeID SET ROWCOUNT 0 GO この例では、オプティマイザの癖と 正確さという点では少々難がありますが、これをScottの方法と比較した結果を次に示します。
いかがですか。1000ページ目を呼び出すという状況はまず考えられないアプリケーションの場合は、このオプティマイザの癖を利用することがそれほど重要だとは思わないでしょうが、負荷の大きなデータベースを想定しているのであれば、tempdb内のスペースとブロッキングが問題になるでしょう(一時テーブルまたはテーブル変数に書き込みを行うときは常にtempdbを操作することになります)。tempdbの使用を最小限に抑えるためにできることは何でもすべきです。そうすればアプリケーションの速度が向上します。また、入出力を減らすために、1ページ目では まとめ ここで見ていただいたように、 それでは、ハッピープログラミング! 著者紹介Greg Hamilton(Greg Hamilton)
|