재우니의 블로그

entity framework 6 버전으로 페이징 처리를 아래와 같이 처리해 봤습니다.

 

public StudentsContainer GetStudents(int currentPage, int recordsPerPage, string sortKey, string sortOrder, string searchfor)
        {

            var pageNumber = currentPage; //현재페이지
            var pageSize = recordsPerPage; //페이지 카운트
            var begin = (pageNumber - 1) * pageSize; //시작 index 부분

            var totalNumberOfRecords = db.Students.Count(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor));

            List<Student> results = null;
            switch (sortOrder)
            {
                case "ASC":
                    switch (sortKey)
                    {
                        case "lastName":
                            results = db.Students.Where(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor))
                                .OrderBy(r => r.LastName)
                                .Skip(begin)
                                .Take(pageSize).ToList();
                            break;
                        case "firstName":
                            results = db.Students.Where(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor)).OrderBy(r => r.FirstMidName).Skip(begin).Take(pageSize).ToList();
                            break;
                    }
                    break;
                case "DESC":
                    switch (sortKey)
                    {
                        case "lastName":
                            results = db.Students.Where(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor)).OrderByDescending(r => r.LastName).Skip(begin).Take(pageSize).ToList();
                            break;
                        case "firstName":
                            results = db.Students.Where(r => searchfor == "null" || r.LastName.Contains(searchfor) || r.FirstMidName.Contains(searchfor)).OrderByDescending(r => r.FirstMidName).Skip(begin).Take(pageSize).ToList();
                            break;
                    }
                    break;
            }

            var students =
                results.Select(
                    r =>
                        new Student
                        {
                            EnrollmentDate = r.EnrollmentDate,
                            FirstMidName = r.FirstMidName,
                            LastName = r.LastName,
                            ID = r.ID
                        }).ToList();

            var studentsContainer = new StudentsContainer { Students = students, RecordCount = totalNumberOfRecords };

            return studentsContainer;
        }

 

 

아래는 위에서 구현한 entity framework 6 환경에서 실행한 자동 랜더링 ms-sql 구문입니다.
실행을 해보니 where 조건으로 특정 시작 index 에서 가져온 다음, top 쿼리로 원하는 개수만큼 리스트를 가져오도록 처리 되어 있습니다.


exec sp_executesql N'SELECT  top (5)
    [Project1].[ID] AS [ID],
    [Project1].[LastName] AS [LastName],
    [Project1].[FirstMidName] AS [FirstMidName],
    [Project1].[EnrollmentDate] AS [EnrollmentDate]
    FROM (


SELECT [Project1].[ID] AS [ID], [Project1].[LastName] AS [LastName], [Project1].[FirstMidName] AS [FirstMidName], [Project1].[EnrollmentDate] AS [EnrollmentDate],
           row_number() OVER (ORDER BY [Project1].[LastName] ASC) AS [row_number]
        FROM (

            SELECT
                [Extent1].[ID] AS [ID],
                [Extent1].[LastName] AS [LastName],
                [Extent1].[FirstMidName] AS [FirstMidName],
                [Extent1].[EnrollmentDate] AS [EnrollmentDate]
                FROM [dbo].[Student] AS [Extent1]
                WHERE (N''null'' = @p__linq__0) OR ([Extent1].[LastName] LIKE @p__linq__1 ESCAPE N''~'') OR ([Extent1].[FirstMidName] LIKE @p__linq__2 ESCAPE N''~'')
            )  AS [Project1]

 

    )  AS [Project1]
    WHERE [Project1].[row_number] > 55
    ORDER BY [Project1].[LastName] ASC',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000),@p__linq__2 nvarchar(4000)',@p__linq__0=N'null',@p__linq__1=N'%null%',@p__linq__2=N'%null%'
go

 

저는 위 처럼 top 쿼리도 좋지만, row_number 를 이용하여 between 함수로 범위 지정해서 가져오는 것을 선호 합니다.
사실 성능을 비교하자면 top 쿼리과 별 반 차이는 없습니다. 여기 블로그를 보면 paging function 인 offset 성능이 매우 좋지 않을 것을 볼 수 있습니다.
블로그 내용이 2012 년도라 지금은 많이 개선이 되어 있지 않을까 생각됩니다.

http://www.mssqlgirl.com/paging-function-performance-in-sql-server-2012.html

 

이 강좌는 cte 사용하는것과 사용하지 않은 것 성능 비교이네요.

http://sqlperformance.com/2015/01/t-sql-queries/pagination-with-offset-fetch

 

참고로 entity framework 버전 6.1.2 이상 버전에서는 offset 페이징 함수로 구현 처리 해 준다고 합니다.
사용할 때, ms-sql 2012 부터 offset 함수를 제공하므로 꼭 확인해 보길 바랍니다.

SELECT
     [TransactionID]
    ,[ProductID]
    ,[ReferenceOrderID]
    ,[ReferenceOrderLineID]
    ,[TransactionDate]
    ,[TransactionType]
    ,[Quantity]
    ,[ActualCost]
    ,[ModifiedDate]
FROM [Production].[TransactionHistoryArchive]
ORDER BY [TransactionID]
OFFSET 5001 ROWS
FETCH NEXT 100 ROWS ONLY


http://erikej.blogspot.kr/2014/12/a-breaking-change-in-entity-framework.html