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이 이상하게 작동하는 코드보다 SQL이 눈에 띄게 중단되는 것이 좋습니다.
IMSoP

5
테이블의 모든 데이터를 선택하고 잘라? 5000000000 개의 행이 있다면? 5000000000 개의 행을 선택하고 각 쿼리에 대해 잘라? CPU 및 서버 메모리에 비효율적입니다.
e-info128

3
2012+는 더 나은 방식으로 구현되었습니다. 답변보기 + Martin Smith
meridius

100

당신은 단순히 이전 페이지에서 볼 수있는 마지막 키 값을 기억하고 사용하여 다음 순서에있는 모든 페이지를 처리 할 경우 TOP (25) ... WHERE Key > @last_key ORDER BY Key또는 - 적절한 인덱스이 효율적으로 탐색했습니다 될 수 있도록 존재하는 경우 실적이 가장 좋은 방법이 될 수 있는 API 커서 그렇지 않은 경우 .

SQL 서버 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에 대한 해당 솔루션은 결과 세트의 마지막 페이지에 대해 작동하지 않습니다.
Bill Karwin

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 서버 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 함수는 하나의 열만 살펴보고 일치하는 행만 모든 열과 함께 리턴하므로 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

쿼리가 두 번 불필요하게 실행되므로 성능 현명한 것으로 보이지 않습니다. 특히 사용자가 상위 페이지로 이동하면 행을 버리기위한 쿼리, 즉 EXCEPT 아래 부분이 길어질수록 시간이 오래 걸립니다.
vanval

2

버전에 따라 직접 할 수는 없지만 해킹과 같은 것을 할 수 있습니다.

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

여기서 'field'가 핵심입니다.


4
총 행 수가 페이지 크기의 배수가 아닌 한 SQL2000에 대한 해당 솔루션은 결과 세트의 마지막 페이지에 대해 작동하지 않습니다.
Bill Karwin

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

이것은 10이 아닌 11 개의 행을 반환합니다.
Aaron Bertrand

1

이 기술을 페이지 매김에 사용합니다. 모든 행을 가져 오지 않습니다. 예를 들어, 내 페이지에 상위 100 개의 행을 표시해야하는 경우 100 with where 절만 가져옵니다. SQL 출력에는 고유 키가 있어야합니다.

테이블에는 다음이 있습니다.

ID, KeyId, Rank

둘 이상의 KeyId에 동일한 순위가 할당됩니다.

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

처음으로 나는 둘 다 0을 전달합니다. 두 번째 타임 패스 1 & 14. 세 번째 타임 패스 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 행을 반환합니다.
Aaron Bertrand

1

기록을 주문하는 데 시간을 낭비하지 않고 가장 좋은 방법은 다음과 같습니다.

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

1 초도 걸리지 않습니다!
큰 테이블에 가장 적합한 솔루션입니다.


0

나는이 답변을 잠시 동안 찾고 있었으며 (일반 쿼리의 경우) ROWCOUNT와 커서를 사용하고 TOP 또는 임시 테이블없이 SQL Server 2000 +에서 다른 방법을 찾았습니다.

를 사용하면 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

당신이 테이블의 끝을 향해 갈 때 나는 이것의 성능을보고 싫어 ...
Aaron Bertrand

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

참고 OFFSET 쿼리 식에서 행을 반환하기 전에 건너 뛸 행 수를 지정합니다 . 시작 행 번호가 아닙니다. 따라서 첫 번째 레코드를 포함하려면 0이어야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.