SQL Serverで結果をページ分割する最良の方法は何ですか


474

結果の合計数も(ページ分割前に)取得したい場合、SQL Server 2000、2005、2008、2012で結果をページ分割するための最良の方法(パフォーマンスに関して)は何ですか?


26
TOPの一部としてオフセットの指定をサポートしていないのはなぜかといつも疑問に思っていました(LIMIT / OFFSETを使用したMySQL / Posgresqlサポートなど)。たとえば、「SELECT TOP x、y ....」という構文を使用できます。ここで、x =行数、y =開始オフセットです。また、下位互換性もあります。
gregmac 2008

3
ちょっと、私も... SQLの2005年のページ
分割

6
@gregmac-SQL Server 2012には現在制限/オフセットがあります。
OO

2
受け入れられたソリューションは、それが最良の方法(パフォーマンスに関して)であることを示していません。大規模なデータセットでそれをバックアップするデータはありますか?
OO

3
@OO:良いベンチマークはここにあります:4guysfromrolla.com/webtech/042606-1.shtml。ただし、シークメソッドは、オフセットベースのページネーションよりも優れています。
Lukas Eder 2013年

回答:


465

結果の総数の取得とページ分割は、2つの異なる操作です。この例のために、処理しているクエリが

SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

この場合、次を使用して結果の総数を決定します。

SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'

...これは非効率に見えるかもしれませんが、すべてのインデックスなどが適切に設定されていると仮定すると、実際にはかなりパフォーマンスが高くなります。

次に、実際の結果をページ形式で取得するには、次のクエリが最も効率的です。

SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
          FROM      Orders
          WHERE     OrderDate >= '1980-01-01'
        ) AS RowConstrainedResult
WHERE   RowNum >= 1
    AND RowNum < 20
ORDER BY RowNum

これにより、元のクエリの行1〜19が返されます。ここで特にWebアプリの場合の素晴らしい点は、返される行番号を除いて、状態を保持する必要がないことです。


37
ROW_NUMBER()はSQL Server 2000には存在しないことに注意してください
John Hunter

6
これは内部クエリからすべての行を返し、外部クエリに基づいてフィルタリングしますか?内側問合せ戻り10万&外側のクエリが返すのみ20:EX用
SoftwareGeek

2
@SoftwareGeek:ストリームを返すサブクエリ(内部クエリ)と考えてください。ストリームは、外部のWHERE句が満たされるまで読み込まれます。行がどのように関係するかは、完全にクエリに依存しますが、オプティマイザは通常、その数を最小限に抑える上で非常に優れています。SQL Server Management Studioでグラフィカルな実行プランビューアを使用する(クエリ/実際の実行プランを含める)は、その点で非常に役立ちます。
mdb

2
わかりました、内部選択で重複した場合(内部結合がある場合など)、RowNumberが異なり、機能しないため、distinctをどのように使用しますか
user217648

10
Microsoftは、SQL 2012に新機能を追加し、ページネーションをMySQLと同様にしました。方法については、このリンクをクリックしてください。それは興味深い記事です:dbadiaries.com/...
アラシュ

512

最後に、Microsoft SQL Server 2012がリリースされました。ページネーションの単純さは本当に気に入っています。ここで回答したような複雑なクエリを使用する必要はありません。

次の10行を取得するには、次のクエリを実行します。

SELECT * FROM TableName ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#using-offset-and-fetch-to-limit-the-rows-戻ってきた

使用する際に考慮すべき重要な点:

  • ORDER BYOFFSET ... FETCH句を使用するには必須です。
  • OFFSET節はで必須FETCHです。は使用できませんORDER BY ... FETCH
  • TOP同じクエリ式OFFSETと組み合わせることはできませんFETCH

12
まだ待っていますLISTAGG()/ GROUP_CONCAT()
ベーコンビット2014年

1
@BaconBitsはでそれを行うための卑劣な方法についてはこの回答を参照してくださいFOR XMLstackoverflow.com/a/273330/429949
リチャードMarskell - Drackir

1
@ RichardMarskell-Drackirには多くの問題がありますFOR XML PATH ('')。まず、XML制御文字をXMLエンティティコードに置き換えます。あなたが持っていないことを望む<>または&あなたのデータに!次に、FOR XML PATH ('')この方法で使用されるのは、実際には文書化されていない構文です。名前付き列または代替要素名を指定することになっています。どちらも行わないことはドキュメントに記載されていないため、動作は信頼できません。第三に、壊れたFOR XML PATH ('')構文を受け入れるほど、MSが実際に必要なように実際の LISTAGG() [ OVER() ]機能を提供する可能性は低くなります。
ベーコンビット

4
恥PERFはとても悪いですmssqlgirl.com/...
ジョン

5
@Jon、そのリンクされたブログ投稿は代表的なものではなく、ある意味では、id列の値を検索してページ結果を返すことに基づいて比較を行います。
Noel Abrahams、

103

信じられないことに、SQL Serverのすべてのバージョンでページ分割を行うための最速の方法について他に回答したものはありません。ここベンチマークしているように、大きなページ番号のオフセットは非常に遅くなる可能性があります。SQLでページ分割を実行するには、まったく異なる、はるかに高速な方法があります。これは、このブログ投稿で説明されているように、「シークメソッド」または「キーセットページネーション」と呼ばれます

SELECT TOP 10 first_name, last_name, score, COUNT(*) OVER()
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

「シーク述語」

@previousScoreそして@previousPlayerId値は、前のページから最後のレコードのそれぞれの値です。これにより、「次の」ページを取得できます。ORDER BY方向がの場合はASC、単に>代わりにます。

上記の方法では、最初に前の40レコードをフェッチしないと、すぐにページ4にジャンプできません。しかし、多くの場合、とにかくそんなにジャンプしたくありません。代わりに、インデックスに応じて、一定の時間でデータをフェッチできるはるかに高速なクエリが得られます。さらに、基礎となるデータが変更されても(たとえば、1ページ目で4ページ目が表示されていても)、ページは「安定」したままです。

これは、たとえば、Webアプリケーションでより多くのデータを遅延読み込みするときにページネーションを実装するための最良の方法です。

「シークメソッド」はキーセットページネーションとも呼ばれます。

ページネーション前の合計レコード

COUNT(*) OVER()ウィンドウ関数を使用すると、「ページネーションの前に」総レコード数をカウントするのに役立ちます。SQL Server 2000を使用している場合は、に対して2つのクエリを使用する必要がありますCOUNT(*)


2
@ user960567:SQL標準OFFSET .. FETCHでオフセットページングを実装しても、以前のROW_NUMBER()トリックで実装しても、キーセットページングは​​オフセットページングよりも常に優れています。
Lukas Eder

21
seekメソッドには3つの問題があります。[1]ユーザーはページにジャンプできません。[2]それは順次キーを想定しています。つまり、誰かがいくつかの3行を削除した場合、10ではなく7アイテムのページを取得しRowNumberます。ページごとに一貫した10アイテムを与えます。[3] pagenumberおよびを想定している既存のグリッドでは機能しませんpagesize
レベッカ

7
@Junto:キーセットページングは​​、すべてのケースに適しているわけではありません。データグリッドには絶対に当てはまりません。しかし、それはFacebookフィードページの無限スクロールのようなシナリオに最適です。新しい投稿が上部に追加されているかどうかは関係ありません。下にスクロールすると、後続のフィード投稿が下部に正しく追加されます。このための完璧な使用例...このようなことは、数値のみを使用してオフセット制限/フェッチを使用して実装することははるか困難です。
Robert Koritnik 2015

4
じゅんと同意する必要があります。このメソッドは、「前へ1 2 3(4)5 6次へ」というかなり標準的なページ分割UIを持つクライアントを完全に排除します。これは、私の経験では
必ずしも正義の

3
キーセットのページネーションの記事はこちら
Stphane 2017

31

SQL Server 2012以降では、ページ区切りを実現するためにOFFSETand FETCH NEXT節を使用できます。

SQL Serverの場合、これを試してください。

SQL Server 2012では、ORDER BY句に新機能が追加され、セットデータの最適化をクエリできるようになりました。これにより、T-SQLで書き込むすべての人のデータページングとSQL Serverの実行プラン全体の作業が容易になります。

前の例で使用されたものと同じロジックを持つT-SQLスクリプトの下。

--CREATING A PAGING WITH OFFSET and FETCH clauses IN "SQL SERVER 2012"
DECLARE @PageNumber AS INT, @RowspPage AS INT
SET @PageNumber = 2
SET @RowspPage = 10 
SELECT ID_EXAMPLE, NM_EXAMPLE, DT_CREATE
FROM TB_EXAMPLE
ORDER BY ID_EXAMPLE
OFFSET ((@PageNumber - 1) * @RowspPage) ROWS
FETCH NEXT @RowspPage ROWS ONLY;

TechNet:SQL Serverによるクエリのページング


この試験で最も正確な回答
Vikrant

17

MSDN:ROW_NUMBER(Transact-SQL)

結果セットのパーティション内の行の連番を返します。各パーティションの最初の行は1から始まります。

次の例は、OrderDateの順序で50から60までの数値を含む行を返します。

WITH OrderedOrders AS
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY FirstName DESC) AS RowNumber, 
        FirstName, LastName, ROUND(SalesYTD,2,1) AS "Sales YTD"
    FROM [dbo].[vSalesPerson]
) 
SELECT RowNumber, 
    FirstName, LastName, Sales YTD 
FROM OrderedOrders 
WHERE RowNumber > 50 AND RowNumber < 60;
  RowNumber FirstName    LastName               SalesYTD
  --- -----------  ---------------------- -----------------
  1   Linda        Mitchell               4251368.54
  2   Jae          Pak                    4116871.22
  3   Michael      Blythe                 3763178.17
  4   Jillian      Carson                 3189418.36
  5   Ranjit       Varkey Chudukatil      3121616.32
  6   José         Saraiva                2604540.71
  7   Shu          Ito                    2458535.61
  8   Tsvi         Reiter                 2315185.61
  9   Rachel       Valdez                 1827066.71
  10  Tete         Mensa-Annan            1576562.19
  11  David        Campbell               1573012.93
  12  Garrett      Vargas                 1453719.46
  13  Lynn         Tsoflias               1421810.92
  14  Pamela       Ansman-Wolfe           1352577.13

15

http://www.codeproject.com/KB/aspnet/PagingLarge.aspxに、さまざまなページング手法の優れた概要があります。

SQL Server 2000でROWCOUNTメソッドを頻繁に使用しました(2005および2008でも機能し、ROW_NUMBERと比較してパフォーマンスを測定するだけです)。 )一意の値。


1
興味深いことに、この記事では、ページングを一定の時間で実行できるseekメソッドについては触れられていません...それでも良い記事
Lukas Eder

6

SQL Server 2000の場合、IDENTITY列を持つテーブル変数を使用してROW_NUMBER()をシミュレートできます。

DECLARE @pageNo int -- 1 based
DECLARE @pageSize int
SET @pageNo = 51
SET @pageSize = 20

DECLARE @firstRecord int
DECLARE @lastRecord int
SET @firstRecord = (@pageNo - 1) * @pageSize + 1 -- 1001
SET @lastRecord = @firstRecord + @pageSize - 1   -- 1020

DECLARE @orderedKeys TABLE (
  rownum int IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
  TableKey int NOT NULL
)

SET ROWCOUNT @lastRecord
INSERT INTO @orderedKeys (TableKey) SELECT ID FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

SET ROWCOUNT 0

SELECT t.*
FROM Orders t
  INNER JOIN @orderedKeys o ON o.TableKey = t.ID
WHERE o.rownum >= @firstRecord
ORDER BY o.rownum

このアプローチは、複数列のキーを持つテーブルに拡張でき、ORを使用することによるパフォーマンスのオーバーヘッドは発生しません(インデックスの使用をスキップします)。欠点は、データセットが非常に大きく、最後のページの近くにある場合に使用される一時スペースの量です。その場合、カーソルのパフォーマンスはテストしませんでしたが、より良い場合があります。

このアプローチは、データの最初のページに対して最適化できることに注意してください。また、TOPはSQL Server 2000の変数を受け入れないため、ROWCOUNTが使用されました。


3

SQL Server 2012でのページングの最良の方法は、ストアドプロシージャでオフセットとフェッチを使用することです。 OFFSETキーワード-order by句でオフセットを使用する場合、クエリはOFFSET n Rowsで指定したレコード数をスキップします。

FETCH NEXTキーワード -Fetch Nextをorder by句だけで使用すると、オフセットなしでページングで表示する行数が返され、SQLはエラーを生成します。以下はその例です。

create procedure sp_paging
(
 @pageno as int,
 @records as int
)
as
begin
declare @offsetcount as int
set @offsetcount=(@pageno-1)*@records
select id,bs,variable from salary order by id offset @offsetcount rows fetch Next @records rows only
end

次のように実行できます。

exec sp_paging 2,3

2

これらは、SQLサーバー側でクエリの結果をページングするための私のソリューションです。これらのアプローチは、SQL Server 2008と2012で異なります。また、1つの列を使用したフィルター処理と並べ替えの概念を追加しました。Gridviewでページング、フィルタリング、および順序付けを行うときに非常に効率的です。

テストする前に、1つのサンプルテーブルを作成し、このテーブルに行を挿入する必要があります(実際には、テーブルフィールドを考慮してWhere句を変更する必要があり、selectの主要部分に結合とサブクエリがある可能性があります)。

Create Table VLT
(
    ID int IDentity(1,1),
    Name nvarchar(50),
    Tel Varchar(20)
)
GO


Insert INTO VLT
VALUES
    ('NAME' + Convert(varchar(10),@@identity),'FAMIL' + Convert(varchar(10),@@identity))
GO 500000

これらすべてのサンプルで、ページごとに200行をクエリし、ページ番号1200の行をフェッチしています。

SQL Server 2008では、CTEの概念を使用できます。そのため、SQL Server 2008以降で2種類のクエリを作成しました

-SQL Server 2008以降

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT 
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1   
  ) AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

GO

SQL Server 2008以降でCTEを使用する2番目のソリューション

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1     
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

-SQL Server 2012以降

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      *  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1         
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
ORDER BY 
    CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
        THEN Data.ID END ASC,
    CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
        THEN Data.ID END DESC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
        THEN Data.Tel END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
        THEN Data.Tel END ASC
OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY;

1

このアプローチを試してください:

SELECT TOP @offset a.*
FROM (select top @limit b.*, COUNT(*) OVER() totalrows 
        from TABLENAME b order by id asc) a
ORDER BY id desc;

1

ユースケースとして、以下は使いやすく高速であるようです。ページ番号を設定するだけです。

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6;
with result as(
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
)
select SalesOrderDetailID, SalesOrderID, ProductID from result
WHERE result.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

CTEなし

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6
SELECT SalesOrderDetailID, SalesOrderID, ProductID
FROM (
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
 ) AS SOD
WHERE SOD.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

1
where 1 = 1は何をしますか?
Errol Paleracio

0

SQL 2000データベースで次のサンプルクエリを使用しましたが、SQL 2005でもうまく機能します。複数の列を使用することで、動的に順序付けられます。私はあなたに言います...これは強力です:)

    ALTER PROCEDURE [dbo].[RE_ListingReports_SelectSummary] 

@CompanyID  int,
@pageNumber     int,
@pageSize   int, 
@sort       varchar(200)
AS

DECLARE @sql nvarchar(4000)
DECLARE @strPageSize nvarchar(20)
DECLARE @strSkippedRows nvarchar(20)
DECLARE @strFields nvarchar(4000)
DECLARE @strFilter nvarchar(4000)
DECLARE @sortBy nvarchar(4000)
DECLARE @strFrom nvarchar(4000)
DECLARE @strID nvarchar(100)

If(@pageNumber < 0)
  SET @pageNumber = 1
SET @strPageSize = CAST(@pageSize AS varchar(20)) 
SET @strSkippedRows = CAST(((@pageNumber - 1) * @pageSize) AS varchar(20))-- For    example if pageNumber is 5  pageSize is 10, then SkippedRows = 40.
SET @strID = 'ListingDbID'
SET @strFields = 'ListingDbID,
ListingID,  
[ExtraRoom]
'
SET @strFrom = ' vwListingSummary '

SET @strFilter = ' WHERE
        CompanyID = ' + CAST(@CompanyID As varchar(20)) 
End
SET @sortBy = ''
if(len(ltrim(rtrim(@sort))) > 0)
SET @sortBy = ' Order By ' + @sort

-- Total Rows Count

SET @sql =  'SELECT Count(' + @strID + ')  FROM ' + @strFROM + @strFilter
EXEC sp_executesql @sql

--// This technique is used in a Single Table pagination
SET @sql = 'SELECT ' + @strFields + ' FROM ' + @strFROM +
    ' WHERE ' + @strID +  ' IN ' + 
   '  (SELECT TOP ' + @strPageSize + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + 
             ' AND  ' + @strID + ' NOT IN ' + '
          (SELECT TOP ' + @strSkippedRows + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + @SortBy + ') ' 
   + @SortBy + ') ' + @SortBy
Print @sql 
EXEC sp_executesql @sql

最良の部分は、同じパラメーターを渡す、つまり同じSQLテキストを生成する場合、後の呼び出しをキャッシュするsp_executesqlです。


0
   CREATE view vw_sppb_part_listsource as 
    select row_number() over (partition by sppb_part.init_id order by sppb_part.sppb_part_id asc ) as idx, * from (
      select 
          part.SPPB_PART_ID
          , 0 as is_rev
          , part.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      left join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
      where prev.SPPB_PART_ID is null 
      union 
      select 
          part.SPPB_PART_ID
          , 1 as is_rev
          , prev.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      inner join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
    ) sppb_part

異なるinit_idになると、idxを再起動します


0

このROW_NUMBER手法では、使用するソート列がない場合CURRENT_TIMESTAMP、次のように使用できます。

SELECT TOP 20 
    col1,
    col2,
    col3,
    col4
FROM (
    SELECT 
         tbl.col1 AS col1
        ,tbl.col2 AS col2
        ,tbl.col3 AS col3
        ,tbl.col4 AS col4
        ,ROW_NUMBER() OVER (
            ORDER BY CURRENT_TIMESTAMP
            ) AS sort_row
    FROM dbo.MyTable tbl
    ) AS query
WHERE query.sort_row > 10
ORDER BY query.sort_row

これは、最大700,000のテーブルサイズでの検索でうまく機能しました。

これにより、レコード11〜30がフェッチされます。


順序付けは保証されているとは考えられないため、ページネーションでは、結果セット内の列の一意のセットで順序付けすることをお勧めします。
Arin Taylor

2
このフェッチレコード11 30に
Ardalan Shahgholi

0
create PROCEDURE SP_Company_List (@pagesize int = -1 ,@pageindex int= 0   ) > AS BEGIN  SET NOCOUNT ON;


    select  Id , NameEn     from Company  ORDER by Id ASC  
OFFSET (@pageindex-1 )* @pagesize   ROWS FETCH NEXt @pagesize ROWS ONLY END  GO

DECLARE   @return_value int

EXEC  @return_value = [dbo].[SP_Company_List]         @pagesize = 1 ,         > @pageindex = 2

SELECT    'Return Value' = @return_value

GO

0

このビットにより、SQL Serverと新しいバージョンのMySQLを使用してページ分割を行うことができ、すべての行に合計行数が含まれます。一意の行の数を数えるためにあなたの鍵を使用します。

WITH T AS
(  
  SELECT TABLE_ID, ROW_NUMBER() OVER (ORDER BY TABLE_ID) AS RN
  , (SELECT COUNT(TABLE_ID) FROM TABLE) AS TOTAL 
  FROM TABLE (NOLOCK)
)

SELECT T2.FIELD1, T2.FIELD2, T2.FIELD3, T.TOTAL 
FROM TABLE T2 (NOLOCK)
INNER JOIN T ON T2.TABLE_ID=T.TABLE_ID
WHERE T.RN >= 100
AND T.RN < 200

コードの内容を説明するコメントを残していただけますか?
Doug F



-19

言語も使用しているドライバーも指定していません。したがって、私はそれを抽象的に説明しています。

  • スクロール可能な結果セット/データセットを作成します。これにはテーブルのプライマリが必要です
  • 最後にジャンプ
  • 行数を要求する
  • ページの先頭にジャンプ
  • ページの最後まで行をスクロールします
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.