11

多くのアプリケーションには、データベース テーブルのデータを一度に 1 ページずつ表示するグリッドがあります。それらの多くは、ユーザーがページごとのレコード数を選択したり、任意の列で並べ替えたり、結果を前後に移動したりすることもできます.

テーブル全体をクライアントに渡さずにクライアントでデータをフィルタリングせずに、このパターンを実装するのに適したアルゴリズムは何ですか。表示したいレコードだけをユーザーに表示するにはどうすればよいでしょうか?

LINQ はソリューションを簡素化しますか?

4

8 に答える 8

11

MS SQL Server 2005 以降では、ROW_NUMBER()が機能しているようです。

T-SQL: ROW_NUMBER() によるページング

DECLARE @PageNum AS INT;
DECLARE @PageSize AS INT;
SET @PageNum = 2;
SET @PageSize = 10;

WITH OrdersRN AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY OrderDate, OrderID) AS RowNum
          ,OrderID
          ,OrderDate
          ,CustomerID
          ,EmployeeID
      FROM dbo.Orders
)

SELECT * 
  FROM OrdersRN
 WHERE RowNum BETWEEN (@PageNum - 1) * @PageSize + 1 
                  AND @PageNum * @PageSize
 ORDER BY OrderDate
         ,OrderID;
于 2008-08-13T18:40:32.220 に答える
7

LINQ を使用するか、LINQ の動作をコピーすることをお勧めします。LINQ の Take および Skip メソッドを使用してページングされたデータを取得するアプリがあります。コードは次のようになります。

MyDataContext db = new MyDataContext();
var results = db.Products
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize);

SQL Server Profiler を実行すると、LINQ がこのクエリを次のような SQL に変換していることがわかります。

SELECT [ProductId], [Name], [Cost], and so on...
FROM (
    SELECT [ProductId], [Name], [Cost], [ROW_NUMBER]
    FROM (
       SELECT ROW_NUMBER() OVER (ORDER BY [Name]) AS [ROW_NUMBER], 
           [ProductId], [Name], [Cost]
       FROM [Products]
    )
    WHERE [ROW_NUMBER] BETWEEN 10 AND 20
)
ORDER BY [ROW_NUMBER]

平易な英語で:
1. 行をフィルタリングし、ROW_NUMBER 関数を使用して必要な順序で行番号を追加します。
2. フィルター (1) を使用して、ページに必要な行番号のみを返します。
3. 行番号でソート (2) します。これは、必要な順序 (この場合は名前順) と同じです。

于 2008-08-13T19:29:28.380 に答える
3

.Net 3.5 のラムダ式と匿名クラスを組み合わせた LINQ は、この種のことを大幅に簡素化します。

データベースのクエリ:

var customers = from c in db.customers
                join p in db.purchases on c.CustomerID equals p.CustomerID
                where p.purchases > 5
                select c;

ページあたりのレコード数:

customers = customers.Skip(pageNum * pageSize).Take(pageSize);

任意の列による並べ替え:

customers = customers.OrderBy(c => c.LastName);

サーバーから選択したフィールドのみを取得:

var customers = from c in db.customers
                join p in db.purchases on c.CustomerID equals p.CustomerID
                where p.purchases > 5
                select new
                {
                    CustomerID = c.CustomerID,
                    FirstName = c.FirstName,
                    LastName = c.LastName
                };

これにより、プロパティにアクセスできる静的に型指定された匿名クラスが作成されます。

var firstCustomer = customer.First();
int id = firstCustomer.CustomerID;

クエリの結果はデフォルトで遅延ロードされるため、実際にデータが必要になるまでデータベースと対話することはありません。また、.Net の LINQ は、行った変更のデータ コンテキストを保持し、変更したフィールドのみを更新することで、更新を大幅に簡素化します。

于 2008-08-15T22:07:40.743 に答える
1

MSSQL2005で使用するソリューションがいくつかあります。

それらの1つはROW_NUMBER()です。しかし、個人的には、ROW_NUMBER()は大きな結果には機能しないため、好きではありません(私が取り組んでいるDBは非常に大きく、1TBを超えるデータが1秒間に数千のクエリを実行します-ご存知のとおり-大きなソーシャルネットワーキングサイト)。

これが私のお気に入りの解決策です。

T-SQLの一種の擬似コードを使用します。

forename、surnameでソートされたユーザーの2ページ目を見つけましょう。各ページには、10個のレコードがあります。

@page = 2 -- input parameter
@size = 10 -- can be optional input parameter

if @page < 1 then begin
    @page = 1 -- check page number
end
@start = (@page-1) * @size + 1 -- @page starts at record no @start

-- find the beginning of page @page
SELECT TOP (@start)
    @forename = forename,
    @surname = surname
    @id = id
FROM
    users
ORDER BY
    forename,
    surname,
    id -- to keep correct order in case of have two John Smith.

-- select @size records starting from @start
SELECT TOP (@size)
    id,
    forename,
    surname
FROM
    users
WHERE
    (forename = @forename and surname = @surname and id >= @id) -- the same name and surname, but bigger id
    OR (forename = @forename and surname > @surname) -- the same name, but bigger surname, id doesn't matter
    OR (forename > @forename) -- bigger forename, the rest doesn't matter
ORDER BY
    forename,
    surname,
    id
于 2008-08-15T21:44:25.533 に答える
1

実際、LINQ には Skip メソッドと Take メソッドがあり、これらを組み合わせて取得するレコードを選択できます。

それらをチェックしてください。

DB の場合: SQL Server 2005 のページネーション

于 2008-08-13T18:43:17.893 に答える
1

オラクルのソリューション:

select * from (
    select a.*, rownum rnum from (
        YOUR_QUERY_GOES_HERE -- including the order by
    ) a
    where rownum <= MAX_ROW
 ) where rnum >= MIN_ROW
于 2008-08-13T19:00:32.320 に答える
0

これについての議論がありますここ

この手法は、150,000 行のデータベースから 78ms でページ番号 100,000 を取得します。

オプティマイザーの知識と SET ROWCOUNT を使用して、要求されたページ内の最初の EmployeeID が開始点のローカル変数に格納されます。次に、@maximumRows で要求された最大レコード数に ROWCOUNT を設定します。これにより、はるかに効率的な方法で結果セットをページングできます。この方法を使用すると、ローカルに作成されたテーブルではなくベース テーブルに直接移動するため、テーブルの既存のインデックスも利用できます。

現在受け入れられている回答よりも優れているかどうかを判断できないことを残念に思います。

于 2008-09-11T16:04:25.703 に答える