SQL Serverの行オフセット


133

SQL Serverで、特定のオフセットから結果を取得する方法はありますか?たとえば、別のタイプのSQLデータベースでは、次のことが可能です。

SELECT * FROM MyTable OFFSET 50 LIMIT 25

結果51-75を取得します。この構成はSQL Serverに存在しないようです。

気にしないすべての行をロードせずにこれを行うにはどうすればよいですか?ありがとう!


オフセットを使用して、次のステートメントをフェッチできます。 youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

回答:


152

の使用は避けSELECT *ます。すべての列であっても、実際に必要な列を指定します。

SQL Server 2005以降

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

SQL Server 2000で大きな結果セットを効率的にページングする

大きな結果セットをページングするためのより効率的な方法


6
すべての列を選択している場合でも、なぜSELECTを避けることを勧めるのですか?
Adam Ness、

12
「col1」、「col2、... colN」よりも入力が簡単でポイントが優れていたので、彼は「*」を使用したと思います
gillonba

9
なぜそれを使用しないのかについてSELECT *は、テーブルの構造が変わってもクエリは実行されますが、結果は異なります。列が追加された場合、これは便利な場合があります(ただし、どこかで名前を付けて使用する必要があります)。列が削除または名前変更された場合、変数が初期化されていないために奇妙な動作をするコードよりも、SQLが目に見える形で壊れる方が良いです。
IMSoP 2013

5
テーブルのすべてのデータを選択してカットしますか?5000000000行がある場合 5000000000行を選択し、クエリごとに切り取りますか?サーバーのCPUとメモリには効率的ではありません。
e-info128 2013

3
2012+は、より適切に実装されていることに注意してください。+ Martin Smithによる回答を参照
meridius

100

すべてのページを順番に処理する場合は、前のページに表示された最後のキー値を覚えておいて、TOP (25) ... WHERE Key > @last_key ORDER BY Keyこれを効率的に検索できる適切なインデックスが存在する場合に使用するのが最も効果的な方法です。APIカーソルがない場合は。

arbitaryページにSQL Server 2005の最適なソリューションを選択するための- 2008 R2は、おそらくあるROW_NUMBERBETWEEN

SQL Server 2012+の場合、このニーズのために拡張されたORDER BY句を使用できます。

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

けれども、それが見られることを残る行い、このオプションがどのようにうまくなります


2
SQL Server Compact 4.0で利用できるようになりました-> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Bart Verkoeijen

13
そろそろ、これをtSQLに追加するときです
JohnFx

3
SQL Server 2012の場合のみ:(
e-info128

22

これは一方向です(SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

これは別の方法です(SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize

最初のものを明確にするために...(@pageSize)は、実際の値のプレースホルダーです。具体的には「TOP 25」を実行する必要があります。SQL Server 2000は、TOP句の変数をサポートしていません。これにより、動的SQLに伴う問題が発生します。
Cowan、

5
SQL2000のこのソリューションは、行の総数がページサイズの倍数にならない限り、結果セットの最後のページでは機能しません。
ビルカーウィン、

10

ROW_NUMBER()関数を使用して、必要なものを取得できます。

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20

7

あるOFFSET .. FETCHSQL Serverの2012年に、しかし、あなたは指定する必要がありますORDER BY列を。

ORDER BY(他の人が示唆しているように)列として渡すことができる明示的な列が本当にない場合は、このトリックを使用できます:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

...または

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

ユーザーが順序を明示的に指定しない場合は、jOOQで使用しています。これにより、追加コストなしでかなりランダムな順序が生成されます。


6

より多くの大きなデータ列を持つテーブルの場合、私は次のようにします。

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

-

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

ROW_NUMBER関数は1つの列のみを参照する必要があり、一致する行のみがすべての列で返されるため、BLOBなどの大きなデータを含むテーブルでのパフォーマンスが大幅に向上します。


5

ページネーションの私の選択を参照してください

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

これはページネーションを解決します;)


3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable

クエリは不必要に2回実行されるため、パフォーマンスは最適とは言えません。特に、ユーザーが上位のページに移動すると、行を破棄するためのクエリ、つまりEXCEPTの下の部分にかかる時間が長くなります。
vanval

2

バージョンによっては、直接実行することはできませんが、次のようなハックを行うことができます

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

ここで、「フィールド」はキーです。


4
SQL2000のこのソリューションは、行の総数がページサイズの倍数にならない限り、結果セットの最後のページでは機能しません。
ビルカーウィン

2

以下は、SQL Server 2012で動作する最初の50レコードを除く25レコードを表示します。

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

あなたはあなたの要件としてIDを置き換えることができます


Plsも、これはSQL SERVER 2012で可能であることを追加
Usman Younas

2

ROW_NUMBER() OVER (ORDER BY)パフォーマンスが非常に低いため、ステートメントを使用するときは注意が必要です。同じことが、共通テーブル式を使用するROW_NUMBER()場合にも当てはまります。次のスニペットを使用していますが、ID付きのテーブル変数を使用してページ番号を提供するよりも少し高速であることが証明されています。

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet

これは11行ではなく、10を返します
アーロン・ベルトラン

1

私はこのテクニックをページ分割に使用しています。すべての行をフェッチするわけではありません。たとえば、ページに上位100行を表示する必要がある場合、100のwhere句のみをフェッチします。SQLの出力には一意のキーが必要です。

テーブルには以下が含まれます。

ID, KeyId, Rank

同じランクが複数のKeyIdに割り当てられます。

SQLは select top 2 * from Table1 where Rank >= @Rank and ID > @Id

初めて、両方に0を渡します。2回目のパス1および14。3回目のパス2および6 ....

10番目のレコードのランクとIDの値が次のレコードに渡されます

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

これにより、システムへのストレスが最小になります


1

SqlServer2005では、次のことができます。

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 

そうじゃないの@Offset + @Limit - 1?@Limitが10の場合、これは11行を返します。
アーロンベルトラン

1

レコードを注文する時間を無駄にすることなくそれを行う最良の方法は次のとおりです。

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

1秒もかかりません!
大きなテーブルに最適なソリューション。


0

私はしばらくの間この回答を検索してきましたが(一般的なクエリの場合)、SQL Server 2000+でROWCOUNTとカーソルを使用し、TOPまたは一時テーブルなしでそれを行う別の方法を見つけました。

SET ROWCOUNT [OFFSET+LIMIT]結果を制限することができ、カーソルを使用して、目的の行に直接移動し、最後までループします。

したがって、クエリは次のようになります。

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0

テーブルの終わりに
アーロンバートランド

0

SQL Server 2012(11.x)以降とAzure SQL Databaseでは、 "fetch_row_count_expression"を使用できます。これと共に、ORDER BY句も使用できます。

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-ver15

Note OFFSET クエリ式から行を返し始める前にスキップする行数を指定します。開始行番号ではありません。したがって、最初のレコードを含めるには0にする必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.