문제

많은 애플리케이션에는 데이터베이스 테이블의 데이터를 한 번에 한 페이지씩 표시하는 그리드가 있습니다.또한 이들 중 다수는 사용자가 페이지당 레코드 수를 선택하고, 열별로 정렬하고, 결과를 앞뒤로 탐색할 수 있도록 해줍니다.

전체 테이블을 클라이언트로 가져온 다음 클라이언트에서 데이터를 필터링하지 않고 이 패턴을 구현하는 좋은 알고리즘은 무엇입니까?사용자에게 표시하려는 레코드만 어떻게 가져오나요?

LINQ는 솔루션을 단순화합니까?

도움이 되었습니까?

해결책

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;

다른 팁

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)을 필터링하세요.
삼.원하는 순서와 동일한 행 번호를 기준으로 (2)를 정렬합니다(이 경우 이름 기준).

데이터베이스에서 페이지 매김을 수행하는 방법에는 기본적으로 두 가지가 있습니다(SQL Server를 사용한다고 가정합니다).

오프셋 사용

다른 사람들은 어떻게 설명했는지 설명했습니다. ROW_NUMBER() OVER() 순위 기능을 사용하여 페이지를 수행할 수 있습니다.SQL Server 2012에 마침내 SQL 표준에 대한 지원이 포함되었다는 점은 언급할 가치가 있습니다. OFFSET .. FETCH 절:

SELECT first_name, last_name, score
FROM players
ORDER BY score DESC
OFFSET 40 ROWS FETCH NEXT 10 ROWS ONLY

SQL Server 2012를 사용 중이고 이전 버전과의 호환성이 문제가 되지 않는 경우, 특수한 경우에 SQL Server에서 이 절이 더 최적으로 실행되므로 이 절을 선호해야 할 것입니다.

SEEK 방법 사용

SQL에서 페이징을 수행하는 완전히 다르고 훨씬 빠르지만 잘 알려지지 않은 방법이 있습니다.이것은 종종 에 설명된 대로 "탐색 방법"이라고 불립니다. 이 블로그 게시물은 여기.

SELECT TOP 10 first_name, last_name, score
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페이지에 있는 동안).

예를 들어, 이는 웹 애플리케이션에서 더 많은 데이터를 지연 로드할 때 페이징을 구현하는 가장 좋은 방법입니다.

참고로 "탐색 방법"이라고도 합니다. 키셋 페이징.

.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는 또한 변경 사항의 데이터 컨텍스트를 유지하고 변경한 필드만 업데이트하여 업데이트를 크게 단순화합니다.

오라클 솔루션:

select * from (
    select a.*, rownum rnum from (
        YOUR_QUERY_GOES_HERE -- including the order by
    ) a
    where rownum <= MAX_ROW
 ) where rnum >= MIN_ROW

MS SQL 2005에서 사용하는 몇 가지 솔루션이 있습니다.

그 중 하나가 ROW_NUMBER()입니다.하지만 개인적으로 저는 ROW_NUMBER()가 큰 결과를 얻지 못하기 때문에 좋아하지 않습니다(제가 작업하는 DB는 정말 큽니다. 1TB가 넘는 데이터가 초당 수천 개의 쿼리를 실행합니다. 아시다시피 대규모 소셜 네트워킹 대지).

제가 가장 좋아하는 솔루션은 다음과 같습니다.

일종의 T-SQL 의사 코드를 사용하겠습니다.

이름, 성으로 정렬된 사용자의 두 번째 페이지를 찾아보겠습니다. 각 페이지에는 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

실제로 LINQ에는 가져올 레코드를 선택하기 위해 결합할 수 있는 Skip 및 Take 메서드가 있습니다.

확인해 보세요.

DB의 경우: SQL Server 2005의 페이지 매김

이에 대한 논의가 있습니다 여기

이 기술은 78ms 내에 150,000줄의 데이터베이스에서 페이지 번호 100,000을 가져옵니다.

최적화 지식과 SET ROWCOUNT를 사용하여 요청된 페이지의 첫 번째 EmployeeID가 시작점의 로컬 변수에 저장됩니다.그런 다음 @maximumRows에서 요청된 최대 레코드 수로 SET ROWCOUNT를 설정합니다.이를 통해 훨씬 더 효율적인 방식으로 결과 집합을 페이징할 수 있습니다.이 방법을 사용하면 로컬로 생성된 테이블이 아닌 기본 테이블로 직접 이동하므로 테이블의 기존 인덱스를 활용할 수도 있습니다.

현재 허용되는 답변보다 나은지 판단할 수 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top