検索結果を n 件ずつ表示する方法は、いろいろとありますが、
一時テーブル&ストアドを利用するのが一番簡単な方法です。
これを Northwind の Products テーブルを使って説明します。
# コマンドをコピー&ペーストして実行しながら読み進められます
USE Northwind
-- 変数宣言、変数へ ch を代入
DECLARE @p1 varchar(40)
SELECT @p1='ch'
--商品名に ch が含まれる商品を検索
SELECT ProductID,ProductName
FROM Products
WHERE ProductName LIKE '%' + @p1 + '%'
検索結果(14件)
| ProductID |
ProductName |
| 1 |
Chai |
| 2 |
Chang |
| 39 |
Chartreuse verte |
| 4 |
Chef Anton's Cajun Seasoning |
| 5 |
Chef Anton's Gumbo Mix |
| 48 |
Chocolade |
| 56 |
Gnocchi di nonna Alice |
| 26 |
Gumbär Gummibärchen |
| 41 |
Jack's New England Clam Chowder |
| 55 |
Pâté chinois |
| 12 |
Queso Manchego La Pastora |
| 34 |
Sasquatch Ale |
| 27 |
Schoggi Schokolade |
| 19 |
Teatime Chocolate Biscuits |
SELECT の実行直後に
と実行すると、検索件数(ch 含む商品なら 14 件)を取得できます。
この検索結果を n 件ずつ表示するには、検索結果に連番を振り、
それを一時テーブルに格納するようにします。
-- 一時テーブルの作成(rowID 列が連番のための列)
CREATE TABLE #t
( rowID int IDENTITY(1,1),
ProductID int,
ProductName varchar(40) )
-- 検索結果(chを含む商品)を一時テーブルへ格納
-- rowID 列には IDENTITY によって連番が自動で割り振られる
DECLARE @p1 varchar(40)
SELECT @p1='ch'
INSERT INTO #t(ProductID,ProductName)
SELECT ProductID,ProductName
FROM Products
WHERE ProductName LIKE '%' + @p1 + '%'
あとは、BETWEEN を使って、取り出したい範囲を指定すれば
任意の場所からデータを取得できます。
-- 検索結果のうち 1件目から5件目を取り出す
SELECT * FROM #t
WHERE rowID BETWEEN 1 AND 5
| rowID |
ProductID |
ProductName |
| 1 |
1 |
Chai |
| 2 |
2 |
Chang |
| 3 |
39 |
Chartreuse verte |
| 4 |
4 |
Chef Anton's Cajun Seasoning |
| 5 |
5 |
Chef Anton's Gumbo Mix |
-- 6件目から10件目を取り出す
SELECT * FROM #t
WHERE rowID BETWEEN 6 AND 10
| rowID | ProductID | ProductName |
| 6 | 48 | Chocolade |
| 7 | 56 | Gnocchi di nonna Alice |
| 8 | 26 | Gumbär Gummibärchen |
| 9 | 41 | Jack's New England Clam Chowder |
| 10 | 55 | Pâté chinois |
-- 確認が完了したら一時テーブルを削除しておく
DROP TABLE #t
■ ページングのためのストアドプロシージャ
以上をまとめて、n 件ずつ取得するためのストアドプロシージャ
n 件(ページサイズ)部分をパラメータ化したもの
-- ストアドプロシージャ名: paging1
CREATE PROCEDURE paging1
@p1 varchar(40), --検索したい文字列
@rcnt int OUTPUT, --検索結果の件数を出力するため
@currentPage int, --現在のページ番号
@pageSize int --ページサイズ
AS
CREATE TABLE #t
( rowID int IDENTITY(1,1),
ProductID int,
ProductName varchar(40) )
INSERT INTO #t(ProductID,ProductName)
SELECT ProductID,ProductName
FROM Products
WHERE ProductName LIKE '%' + @p1 + '%'
-- 処理件数(=検索結果の件数)を出力パラメータへ代入
SELECT @rcnt=@@ROWCOUNT
-- 一時テーブルから現在のページ番号に応じて n 件分取り出す
DECLARE @first int,@last int
SELECT @first=((@currentPage-1)*@pageSize)+1
SELECT @last=(@currentPage*@pageSize)
SELECT ProductID,ProductName
FROM #t
WHERE rowID BETWEEN @first AND @last
■ ストアドプロシージャの動作確認
-- ch を含む商品の検索、1ページ目、5件ずつ表示
DECLARE @rcnt int
EXEC paging1 'ch',@rcnt output,1,5
SELECT @rcnt
-- 2ページ目
DECLARE @rcnt int
EXEC paging1 'ch',@rcnt output,2,5
SELECT @rcnt
<その他>
・一時テーブルに PRIMARY KEY 制約をつけるか否か
PRIMARY KEY 制約をつけると、検索結果を INSERT する
ときのパフォーマンスが悪くなりますが、BETWEEN での検索が
速くなります。前者は制約チェックのオーバーヘッド、後者は
Index Seek の恩恵です。
どっちが良いかは難しいところですが...
ページ移動のたびにストアドが呼び出されて、検索が再実行
(一時テーブルへの INSERT し直し)されることを考えると、
PRIMARY KEY 制約をつけない方が良いような気がします。
・テーブルサイズが大きい場合
LIKE '% によるテーブルスキャンがパフォーマンス低下の原因と
なる可能性が高いので、 LIKE '% で検索対象となる列を別テーブ
ルへ分割したり、LIKE '% を使わなくても済むような作り込みが必
要です。
案1. 「キーワード登録用のキーワード テーブルを別途作る」、
案2 「頻繁に検索されるキーワードの検索結果を永続テーブルと
して定期的にバッチで作成する+ユーザーが入力した検索キー
ワードをログへ残し、頻繁に使用されるものを把握する」、
案3. 「検索結果の上限を設けてしまう(上位150件のみ表示など)」
などかな?
・ADO.NET の DataGrid でカスタムページングを行ないたい場合
参考資料(dotnetjunkies の 2つ)のように、ページ移動ボタンを
LinkButton で自作します。ページサイズは DataGrid の PageSize
プロパティから取得できますが、PageIndexChanged イベントハン
ドラは利用せず、LinkButton の Click イベントハンドラで処理します。
・ADO.NET の DataGrid のカスタムページングで VirtualItemCount
プロパティと PageIndexChanged イベントハンドラを使いたい場合
VirtualItemCount と PageIndexChanged を使うと、ページ移動ボタン
を自作することなく簡単にカスタムページングが実現できるのです
が、VirtualItemCount プロパティは検索結果の件数を事前に設定
しなければなりません。このため、OUTPUT パラメータで出力し
た @@ROWCOUNT は利用できません。
事前に件数を取得するには、通常の検索を行なう前に同じ検索
条件で COUNT(*) を実行するようにします。
この方法は、大澤さんの PASSJ 連載「ステップアップ!ADO.NET」
の第4回が参考になります。
「データベースからの読み込み ~その3:ページ操作、並べ替え、そして検索~」
http://www.sqlpassj.org/bunkakai/web/series/ado/04.aspx
ただし、この方法では検索を 2 回実行することになるので、
検索対象となるテーブルがデータバッファキャッシュに入りきら
ないぐらい大きい場合には注意が必要です。
<参考資料>
・dotnetjunkies: ASP.NET DataGrid Paging Part 2 - Custom Paging
http://www.dotnetjunkies.com/Article/EA868776-D71E-448A-BC23-B64B871F967F.dcik
・dotnetjunkies: ASP.NET DataGrid Paging - Custom Paging w/ Caching & Numeric Links
http://www.dotnetjunkies.com/Tutorial/07FB766A-35F2-4691-923A-D5F1BA3A12DD.dcik
・DBマガジン 2004年1月号 「ADO.NET 基礎と実践 TIPS」