Banyak aplikasi yang memiliki grid yang menampilkan data dari tabel database satu halaman pada satu waktu. Banyak dari mereka juga membiarkan pengguna memilih jumlah record per halaman, mengurutkan berdasarkan kolom, dan menavigasi bolak-balik melalui hasil.
Apa's sebuah algoritma yang baik untuk menerapkan pola ini tanpa membawa seluruh tabel untuk klien dan kemudian menyaring data pada klien. Bagaimana anda membawa catatan-catatan yang ingin anda tampilkan ke pengguna?
Apakah LINQ menyederhanakan solusi?
Pada MS SQL Server 2005 ke atas, ROW_NUMBER() tampaknya bekerja:
T-SQL: Paging dengan 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;
I'd merekomendasikan untuk menggunakan LINQ, atau mencoba untuk menyalin apa yang dilakukannya. I've punya sebuah aplikasi di mana saya menggunakan LINQ Mengambil dan Melewatkan metode untuk mengambil paged data. Kode terlihat seperti ini:
MyDataContext db = new MyDataContext();
var results = db.Products
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize);
Menjalankan SQL Server Profiler mengungkapkan bahwa LINQ adalah konversi ini query ke SQL mirip dengan:
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]
Dalam bahasa inggris:
Pada dasarnya ada dua cara untuk melakukan pagination dalam database (saya'm asumsi anda're menggunakan SQL Server):
Orang lain telah menjelaskan bagaimana ROW_NUMBER() LEBIH dari()
peringkat fungsi dapat digunakan untuk melakukan halaman. It's worth menyebutkan bahwa SQL Server 2012 akhirnya termasuk dukungan untuk SQL standar OFFSET .. AMBIL
ayat:
SELECT first_name, last_name, score
FROM players
ORDER BY score DESC
OFFSET 40 ROWS FETCH NEXT 10 ROWS ONLY
Jika anda're menggunakan SQL Server 2012 dan mundur kompatibilitas tidak masalah, anda mungkin harus memilih klausul ini yang akan dieksekusi lebih optimal dengan SQL Server dalam kasus sudut.
Ada yang sama sekali berbeda, jauh lebih cepat, tapi kurang dikenal cara untuk melakukan paging di SQL. Hal ini sering disebut sebagai "carilah metode" seperti yang dijelaskan dalam posting blog ini di sini.
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
The @previousScore
dan @previousPlayerId
nilai-nilai ini adalah nilai masing-masing dari catatan terakhir dari halaman sebelumnya. Hal ini memungkinkan anda untuk mengambil "next" di halaman. Jika ORDER BY
arah ASC
, hanya menggunakan >
sebagai gantinya.
Dengan metode di atas, anda tidak dapat langsung melompat ke halaman 4 tanpa terlebih dahulu diambil sebelumnya 40 catatan. Tapi sering kali, anda tidak ingin untuk melompat sejauh itu pula. Sebaliknya, anda mendapatkan jauh lebih cepat query yang mungkin dapat mengambil data dalam waktu yang konstan, tergantung pada pengindeksan. Plus, halaman anda tetap "stabil", tidak peduli apakah yang mendasari perubahan data (misalnya pada halaman 1, sementara anda're pada halaman 4).
Ini adalah cara terbaik untuk menerapkan paging ketika malas memuat lebih banyak data di aplikasi web, misalnya.
Catatan, "carilah metode" juga disebut keyset paging.
LINQ dikombinasikan dengan ekspresi lambda dan anonim di kelas .Net 3.5 hugely menyederhanakan hal semacam ini.
Query database:
var customers = from c in db.customers
join p in db.purchases on c.CustomerID equals p.CustomerID
where p.purchases > 5
select c;
Jumlah record per halaman:
customers = customers.Skip(pageNum * pageSize).Take(pageSize);
Penyortiran oleh setiap kolom:
customers = customers.OrderBy(c => c.LastName);
Mendapatkan hanya bidang yang dipilih dari server:
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
};
Hal ini menciptakan statis diketik anonim kelas di mana anda dapat mengakses properties:
var firstCustomer = customer.First();
int id = firstCustomer.CustomerID;
Hasil dari query yang malas-dimuat secara default, jadi anda tidak't berbicara ke database sampai anda benar-benar membutuhkan data. LINQ dalam .Net juga sangat menyederhanakan update dengan menjaga datacontext dari setiap perubahan yang anda buat, dan hanya memperbarui bidang yang anda mengubah.
Ada beberapa solusi yang saya gunakan dengan MS SQL 2005.
Salah satu dari mereka adalah ROW_NUMBER(). Tapi, secara pribadi, saya don't seperti ROW_NUMBER() karena itu doesn't bekerja untuk hasil yang besar (DB yang saya kerjakan adalah benar-benar besar-lebih dari 1TB data berjalan ribuan pertanyaan di kedua-anda tahu-besar situs jejaring sosial).
Berikut ini adalah favorit saya solusi.
Saya akan menggunakan jenis pseudo kode T-SQL.
Let's menemukan halaman 2 dari pengguna diurutkan berdasarkan nama kecil, nama keluarga, di mana setiap halaman memiliki 10 catatan.
@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
Ada diskusi tentang ini di Sini
Teknik mendapat nomor halaman 100.000 dari 150.000 line database di 78ms
Menggunakan optimizer pengetahuan dan MENGATUR ROWCOUNT, pertama EmployeeID di halaman yang diminta disimpan dalam variabel lokal untuk titik awal. Selanjutnya, MENGATUR ROWCOUNT untuk jumlah maksimum data yang diminta di @maximumRows. Hal ini memungkinkan paging hasil yang ditetapkan dalam cara yang jauh lebih efisien. Menggunakan metode ini juga mengambil keuntungan dari pra-indeks yang ada di atas meja itu pergi langsung ke tempat meja dan tidak dibuat secara lokal dan meja.
Saya takut saya tidak dapat menilai apakah itu lebih baik dari saat ini jawaban yang diterima.