Çift sorgulama olmadan MySQL sayfalandırma?


115

Bir MySQL sorgusundan sonuç sayısını almanın ve aynı zamanda sonuçları sınırlamanın bir yolu olup olmadığını merak ediyordum.

Sayfalandırmanın çalışma şekli (anladığım kadarıyla), önce şöyle bir şey yapıyorum:

query = SELECT COUNT(*) FROM `table` WHERE `some_condition`

Satır_sayısını (sorgu) aldıktan sonra, sonuçların sayısına sahibim. Ancak sonuçlarımı gerçekten sınırlamak için, aşağıdaki gibi ikinci bir sorgu yapmam gerekiyor:

query2 = SELECT COUNT(*) FROM `table` WHERE `some_condition` LIMIT 0, 10

Sorum: Her ikisi de verilecek toplam sonuç sayısını almak VE tek bir sorguda döndürülen sonuçları sınırlamak için zaten var mı? Veya bunu yapmanın daha verimli bir yolu. Teşekkürler!


8
Sorgu2'de COUNT (*) olmasa da
dlofrodloh

Yanıtlar:


66

Hayır, sayfalandırmak isteyen kaç uygulamanın bunu yapması gerekiyor. Sorguyu iki kez yapmasına rağmen güvenilir ve kurşun geçirmezdir. Ancak sayımı birkaç saniyeliğine önbelleğe alabilirsiniz ve bu çok yardımcı olacaktır.

Diğer yol ise SQL_CALC_FOUND_ROWScümle kullanmak ve sonra aramaktır SELECT FOUND_ROWS(). FOUND_ROWS()Aramayı sonradan yapmanız gerektiği gerçeği dışında , bununla ilgili bir sorun var: MySQL'de bu, sorguları etkileyen , büyük tablolarda iki sorgunun naif yaklaşımından çok daha yavaş hale getiren bir hata var ORDER BY.


2
Bununla birlikte, iki sorguyu bir işlem içinde yapmadığınız sürece, yarış koşullarının kanıtı değildir. Yine de bu genellikle bir sorun değildir.
NickZoic

"Güvenilir" derken, SQL'in kendisinin her zaman istediğiniz sonucu döndüreceğini ve "kurşun geçirmez" derken, kullanabileceğiniz SQL'i engelleyen MySQL hatalarının olmadığını kastettim. Bahsettiğim hataya göre, SQL_CALC_FOUND_ROWS'u ORDER BY ve LIMIT ile kullanmanın aksine.
staticsan

5
Karmaşık sorgularda, aynı sorgudaki sayıyı almak için SQL_CALC_FOUND_ROWS kullanmak neredeyse her zaman iki ayrı sorgu yapmaktan daha yavaş olacaktır. Bunun nedeni, sınıra bakılmaksızın tüm satırların tam olarak alınması gerektiği anlamına gelir, bu durumda yalnızca LIMIT yan tümcesinde belirtilenler döndürülür. Bağlantıları olan cevabıma da bakınız.
thomasrutter

Buna ihtiyaç duyduğunuz nedene bağlı olarak, toplam sonuçları almamayı da düşünmek isteyebilirsiniz. Otomatik sayfalama yöntemlerini uygulamak daha yaygın bir uygulama haline geliyor. Facebook, Twitter, Bing ve Google gibi siteler bu yöntemi yıllardır kullanıyor.
Thomas B

68

Neredeyse hiç iki sorgu yapmam.

Gerekenden bir tane daha fazla satır döndürün, sayfada yalnızca 10'u görüntüleyin ve gösterilenden daha fazlası varsa, "İleri" düğmesini görüntüleyin.

SELECT x, y, z FROM `table` WHERE `some_condition` LIMIT 0, 11
// iterate through and display 10 rows.

// if there were 11 rows, display a "Next" button.

Sorgunuz önce en alakalı sırayla dönmelidir. Muhtemelen çoğu insan 412'de 236. sayfaya gitmeyi umursamayacak.

Bir Google araması yaptığınızda ve sonuçlarınız ilk sayfada olmadığında, muhtemelen dokuza değil ikinci sayfaya gidersiniz.


42
Aslında, bir Google sorgusunun ilk sayfasında bulamazsam, genellikle dokuzuncu sayfaya atlarım.
Phil

3
@Phil Bunu daha önce duydum ama neden yapıyorsun?
TK123

5
Biraz geç, ama işte benim gerekçem. Bazı aramalarda, arama motoru için optimize edilmiş bağlantı çiftlikleri hakimdir. Dolayısıyla ilk birkaç sayfa, 1 numaralı pozisyon için mücadele eden farklı çiftliklerdir, faydalı sonuç muhtemelen yine de sorgu ile ilişkilidir, sadece en üstte değil.
Phil

4
COUNTbir toplama işlevidir. Sayımı ve tüm sonuçları tek bir sorguda nasıl döndürürsünüz ? Yukarıdaki sorgu, neye LIMITayarlanmış olursa olsun, yalnızca 1 satır döndürecektir . Eğer eklerseniz GROUP BY, tüm sonuçlar döndürürüz ama COUNTyanlış olacaktır
pixelfreak

2
Bu, Percona tarafından önerilen yaklaşımlardan biridir: percona.com/blog/2008/09/24/…
techdude

27

Çift sorgulamadan kaçınmanın diğer bir yaklaşımı, geçerli sayfanın tüm satırlarını önce bir LIMIT yan tümcesi kullanarak getirmek, ardından maksimum satır sayısı alınmışsa yalnızca ikinci bir COUNT (*) sorgusu yapmaktır.

Çoğu uygulamada, en olası sonuç, tüm sonuçların tek bir sayfaya sığması ve sayfalandırma yapmak zorunda kalmanın normdan çok istisna olması olacaktır. Bu durumlarda, ilk sorgu maksimum sonuç sayısını almayacaktır.

Örneğin, bir yığın aşımı sorusundaki yanıtlar nadiren ikinci bir sayfaya taşınır. Bir yanıtla ilgili yorumlar, hepsini göstermek için gerekli olan 5 sınırını nadiren aşar.

Dolayısıyla bu uygulamalarda, önce bir LIMIT ile bir sorgu yapabilirsiniz ve sonra bu sınıra ulaşılmadığı sürece, ikinci bir COUNT (*) sorgusu yapmaya gerek kalmadan tam olarak kaç satır olduğunu bilirsiniz - ki durumların çoğunu kapsar.


1
@thomasrutter Aynı yaklaşıma sahiptim, ancak bugün onunla bir kusur keşfettim. Sonuçların son sayfası bu durumda sayfalama verilerine sahip olmayacaktır. Örneğin, her sayfanın 25 sonucu olması gerektiğini varsayalım, son sayfada muhtemelen o kadar çok sonuç olmayacak, diyelim ki 7'ye sahip olacak ... bu sayımın (*) asla çalıştırılmayacağı ve dolayısıyla sayfalandırma görüntülenmeyeceği anlamına gelir. kullanıcı.
duellsy

2
Hayır - diyorsan, 200 sonuç gelir, sonraki 25'i sorgularsın ve yalnızca 7 geri alırsın, bu da toplam sonuç sayısının 207 olduğunu ve bu nedenle COUNT (*) ile başka bir sorgu yapmanız gerekmediğini gösterir. çünkü ne söyleyeceğini zaten biliyorsun. Sayfalandırmayı göstermek için ihtiyacınız olan tüm bilgilere sahipsiniz. Sayfalandırmanın kullanıcıya gösterilmemesi ile ilgili bir problem yaşıyorsanız, başka bir yerde bir hata var demektir.
thomasrutter

15

Çoğu durumda, bunu iki ayrı sorguda yapmak, bir sorguda yapmaktan çok daha hızlıdır ve daha az kaynak gerektirir, ancak bu sezgiye aykırı görünse de.

SQL_CALC_FOUND_ROWS kullanırsanız, büyük tablolar için sorgunuzu iki sorgu yürütmekten çok daha yavaş, önemli ölçüde yavaşlatır; ilki COUNT (*) ve ikincisi de LIMIT ile. Bunun nedeni, SQL_CALC_FOUND_ROWS'un LIMIT yan tümcesinin önceki yerine satırlar getirildikten sonra uygulanmasına neden olmasıdır , böylece limitleri uygulamadan önce olası tüm sonuçlar için tüm satırı alır. Bu, verileri gerçekten getirdiği için bir dizinle karşılanamaz.

İki sorgu yaklaşımını kullanırsanız, ilki yalnızca COUNT (*) alır ve gerçekte ve gerçek verileri almazsa, bu çok daha hızlı bir şekilde karşılanabilir çünkü genellikle dizinleri kullanabilir ve gerçek satır verilerini almak zorunda değildir. baktığı her satır. Daha sonra, ikinci sorgunun yalnızca ilk $ offset + $ limit satırlarına bakması ve sonra geri dönmesi gerekir.

MySQL performans blogundaki bu gönderi, bunu daha da açıklıyor:

http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

Sayfalandırmayı optimize etme hakkında daha fazla bilgi için bu gönderiye ve bu gönderiye bakın .


2

Cevabım geç olabilir, ancak ikinci sorguyu (sınırlı olarak) atlayabilir ve bilgileri arka uç komut dosyanız aracılığıyla filtreleyebilirsiniz. Örneğin PHP'de şöyle bir şey yapabilirsiniz:

if($queryResult > 0) {
   $counter = 0;
   foreach($queryResult AS $result) {
       if($counter >= $startAt AND $counter < $numOfRows) {
            //do what you want here
       }
   $counter++;
   }
}

Ancak elbette, dikkate almanız gereken binlerce kayıt olduğunda, çok hızlı bir şekilde verimsiz hale gelir. Önceden hesaplanmış sayı, araştırmak için iyi bir fikir olabilir.

İşte konuyla ilgili güzel bir okuma: http://www.percona.com/ppc2009/PPC2009_mysql_pagination.pdf


Link öldü, sanırım bu doğru olan: percona.com/files/presentations/ppc2009/… . Düzenlemeyecek, çünkü olduğundan emin değilim.
hectorg87

1
query = SELECT col, col2, (SELECT COUNT(*) FROM `table`) AS total FROM `table` WHERE `some_condition` LIMIT 0, 10

16
Bu sorgu yalnızca tablodaki toplam kayıt sayısını döndürür; koşulla eşleşen kayıtların sayısı değil.
Lawrence Barsanti

1
Sayfalandırma için gerekli olan toplam kayıt sayısıdır (@Lawrence).
imme

Oh, peki, sadece whereiç sorguya cümle ekle ve sayfalı sonuçların yanında doğru "toplamı" elde edersin (sayfa, limitcümle ile seçilir
Erenor Paz

alt sorgu sayısı (*) aynı where cümlesi gerektirir, aksi takdirde doğru sonuç sayısını döndürmez
AKrush95

1

2020'de bir cevap arayan herkes için. MySQL belgelerine göre:

"SQL_CALC_FOUND_ROWS sorgu değiştiricisi ve ona eşlik eden FOUND_ROWS () işlevi , MySQL 8.0.17 itibarıyla kullanımdan kaldırılmıştır ve gelecekteki bir MySQL sürümünde kaldırılacaktır. Bunun yerine, sorgunuzu LIMIT ile yürütmeyi ve ardından COUNT (*) ile ikinci bir sorgu yapmayı düşünebilirsiniz. ve ek satırların olup olmadığını belirlemek için LIMIT olmadan. "

Sanırım bu onu çözüyor.

https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_found-rows


0

Sorgunun çoğunu bir alt sorguda yeniden kullanabilir ve bir tanımlayıcı olarak ayarlayabilirsiniz. Örneğin, çalışma zamanına göre 's' harfini içeren filmleri bulan bir film sorgusu sitemde şöyle görünecektir.

SELECT Movie.*, (
    SELECT Count(1) FROM Movie
        INNER JOIN MovieGenre 
        ON MovieGenre.MovieId = Movie.Id AND MovieGenre.GenreId = 11
    WHERE Title LIKE '%s%'
) AS Count FROM Movie 
    INNER JOIN MovieGenre 
    ON MovieGenre.MovieId = Movie.Id AND MovieGenre.GenreId = 11
WHERE Title LIKE '%s%' LIMIT 8;

Bir veritabanı uzmanı olmadığımı ve birinin bunu biraz daha iyi optimize edebileceğini umduğumu unutmayın. Onu doğrudan SQL komut satırı arayüzünden çalıştırmaya devam ettiği için her ikisi de dizüstü bilgisayarımda ~ 0,02 saniye sürüyor.


-14
SELECT * 
FROM table 
WHERE some_condition 
ORDER BY RAND()
LIMIT 0, 10

3
Bu soruya cevap vermiyor ve rand'ın emri gerçekten kötü bir fikir.
Dan Walmsley
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.