OFFSET / FETCH NEXT'ten toplam satır sayısını alma


92

Bu nedenle, web sitemde sayfalama uygulamak istediğim bir dizi kaydı döndüren bir işlevim var. Bunu başarmak için SQL Server 2012'de Offset / Fetch Next'i kullanmam önerildi. Web sitemizde, toplam kayıt sayısını ve o sırada hangi sayfada olduğunuzu listeleyen bir alan var.

Önceden, tüm kayıt setini alıyordum ve sayfalamayı programatik olarak oluşturabiliyordum. Ancak SADECE FETCH NEXT X SIRA ile SQL yöntemini kullandığımda, yalnızca X satırları geri veriliyor, bu nedenle toplam kayıt setimin ne olduğunu ve minimum ve maksimum sayfalarımı nasıl hesaplayacağımı bilmiyorum. Bunu yapmanın tek yolu, işlevi iki kez çağırmak ve ilkinde bir dizi satır saymak, ardından ikinciyi FETCH NEXT ile çalıştırmaktır. Sorguyu iki kez çalıştırmamın daha iyi bir yolu var mı? Performansı yavaşlatmaya değil hızlandırmaya çalışıyorum.

Yanıtlar:


115

Kullanabilirsiniz COUNT(*) OVER()... işte hızlı bir örnek sys.all_objects:

DECLARE 
  @PageSize INT = 10, 
  @PageNum  INT = 1;

SELECT 
  name, object_id, 
  overall_count = COUNT(*) OVER()
FROM sys.all_objects
ORDER BY name
  OFFSET (@PageNum-1)*@PageSize ROWS
  FETCH NEXT @PageSize ROWS ONLY;

Ancak, bu küçük veri kümeleri için ayrılmalıdır; daha büyük setlerde performans berbat olabilir. Daha iyi alternatifler için bu Paul White makalesine bakın , dizine alınmış görünümleri koruma (yalnızca sonuç filtrelenmemişse veya WHEREönceden hükümleri biliyorsanız çalışır ) ve ROW_NUMBER()hileler kullanma .


44
3.500.000 kayıt içeren bir tabloda, COUNT (*) OVER () 1 dakika 3 saniye sürdü. Aşağıda James Moberg tarafından açıklanan yaklaşımın aynı veri setini geri getirmesi 13 saniye sürdü. Eminim, Sayma yaklaşımı daha küçük veri kümeleri için iyi çalışır, ancak gerçekten büyümeye başladığınızda önemli ölçüde yavaşlar.
matthew_360

Ya da sadece COUNT (1) OVER () kullanabilirsiniz, çünkü bu tablodan gerçek verileri okumak zorunda olmadığından daha hızlıdır, örneğin count (*) '
un

1
@AaronBertrand Gerçekten mi? Bu, ya tüm sütunları içeren bir dizine sahip olduğunuz ya da bunun 2008R2'den bu yana çok iyileştirildiği anlamına gelmelidir. Bu versiyonda, count (*) sıralı olarak çalışır, yani ilk * (olduğu gibi: tüm sütunlarda olduğu gibi) seçilir, sonra sayılır. Bir sayma (1) yaptıysanız, gerçek verileri okumaktan çok daha hızlı olan bir sabit seçersiniz.
ldx

5
@idx Hayır, 2008 R2'de de böyle çalışmadı, üzgünüm. 6.5'ten beri SQL Server kullanıyorum ve motorun hem COUNT (*) hem de COUNT (1) için en dar dizini tarayacak kadar akıllı olmadığı bir zamanı hatırlamıyorum. Kesinlikle 2000'den beri değil. Ama hey, benim bir 2008 R2 örneğim var, var olduğunu iddia ettiğiniz bu farkı gösteren SQLfiddle üzerinde bir repro kurabilir misiniz? Denemekten mutluyum.
Aaron Bertrand

2
bir sql server 2016 veritabanında, yaklaşık 25 milyon satırlık bir tabloda arama yaparak, yaklaşık 3000'den fazla sonucu sayfalayarak (tablo değerli bir işlev dahil olmak üzere birkaç birleştirme ile) bu milisaniyeler sürdü - harika!
jkerak

142

COUNT ( ) OVER () yöntemini kullanırken bazı performans sorunlarıyla karşılaştım . (Sunucu muydu emin değilim çünkü 10 kaydı döndürmek 40 saniye sürdü ve daha sonra herhangi bir sorun yaşamadı.) Bu teknik, COUNT ( ) OVER () kullanmak zorunda kalmadan her koşulda çalıştı ve aynı şey:

DECLARE 
    @PageSize INT = 10, 
    @PageNum  INT = 1;

WITH TempResult AS(
    SELECT ID, Name
    FROM Table
), TempCount AS (
    SELECT COUNT(*) AS MaxRows FROM TempResult
)
SELECT *
FROM TempResult, TempCount
ORDER BY TempResult.Name
    OFFSET (@PageNum-1)*@PageSize ROWS
    FETCH NEXT @PageSize ROWS ONLY

32
COUNT (*) değerini bir değişkene kaydetme olasılığı olsaydı gerçekten harika olurdu. Bunu Depolanan Prosedürümün ÇIKIŞ parametresi olarak ayarlayabilirim. Herhangi bir fikir?
Ka

1
Sayımı ayrı bir tabloda almanın bir yolu var mı? Önceki SELECT ifadesi için yalnızca "TempResult" kullanabilirsiniz.
matthew_360

4
Bu neden bu kadar iyi çalışıyor? İlk CTE'de, tüm satırlar seçilir, ardından getirme tarafından ayrıştırılır. İlk CTE'deki tüm satırı seçmenin işleri önemli ölçüde yavaşlatacağını tahmin etmiştim. Her durumda, bunun için teşekkürler!
jbd

1
benim durumumda COUNT (1) OVER () 'dan yavaşladı .. belki seçimdeki bir işlev nedeniyle.
Tiju John

1
Bu, satırlar milyonlarca olduğunda küçük veritabanı için mükemmel çalışır ve çok fazla zaman alır.
Kiya

1

Dayanarak James Moberg cevabı :

Row_Number()SQL server 2012'niz yoksa ve OFFSET kullanamıyorsanız , bu alternatif bir kullanımdır

DECLARE 
    @PageNumEnd INT = 10, 
    @PageNum  INT = 1;

WITH TempResult AS(
    SELECT ID, NAME
    FROM Tabla
), TempCount AS (
    SELECT COUNT(*) AS MaxRows FROM TempResult
)

select * 
from
(
    SELECT
     ROW_NUMBER() OVER ( ORDER BY PolizaId DESC) AS 'NumeroRenglon', 
     MaxRows, 
     ID,
     Name
    FROM TempResult, TempCount

)resultados
WHERE   NumeroRenglon >= @PageNum
    AND NumeroRenglon <= @PageNumEnd
ORDER BY NumeroRenglon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.