LIMIT na poziomie SQL
Aby ograniczyć rozmiar zestawu wyników zapytania SQL, możesz użyć składni SQL:008:
SELECT title
FROM post
ORDER BY created_on DESC
OFFSET 50 ROWS
FETCH NEXT 50 ROWS ONLY
który działa na Oracle 12, SQL Server 2012 lub PostgreSQL 8.4 lub nowszych wersjach.
W przypadku MySQL możesz użyć klauzul LIMIT i OFFSET:
SELECT title
FROM post
ORDER BY created_on DESC
LIMIT 50
OFFSET 50
Zaletą korzystania z paginacji na poziomie SQL jest to, że plan wykonania bazy danych może wykorzystywać te informacje.
Tak więc, jeśli mamy indeks na created_on
kolumna:
CREATE INDEX idx_post_created_on ON post (created_on DESC)
I wykonujemy następujące zapytanie, które używa LIMIT
klauzula:
EXPLAIN ANALYZE
SELECT title
FROM post
ORDER BY created_on DESC
LIMIT 50
Widzimy, że silnik bazy danych używa indeksu, ponieważ optymalizator wie, że należy pobrać tylko 50 rekordów:
Execution plan:
Limit (cost=0.28..25.35 rows=50 width=564)
(actual time=0.038..0.051 rows=50 loops=1)
-> Index Scan using idx_post_created_on on post p
(cost=0.28..260.04 rows=518 width=564)
(actual time=0.037..0.049 rows=50 loops=1)
Planning time: 1.511 ms
Execution time: 0.148 ms
Maksymalne wiersze instrukcji JDBC
Według setMaxRows
Dokumentacja Java
:
To niezbyt uspokajające!
Tak więc, jeśli wykonamy następujące zapytanie na PostgreSQL:
try (PreparedStatement statement = connection
.prepareStatement("""
SELECT title
FROM post
ORDER BY created_on DESC
""")
) {
statement.setMaxRows(50);
ResultSet resultSet = statement.executeQuery();
int count = 0;
while (resultSet.next()) {
String title = resultSet.getString(1);
count++;
}
}
W logu PostgreSQL otrzymujemy następujący plan wykonania:
Execution plan:
Sort (cost=65.53..66.83 rows=518 width=564)
(actual time=4.339..5.473 rows=5000 loops=1)
Sort Key: created_on DESC
Sort Method: quicksort Memory: 896kB
-> Seq Scan on post p (cost=0.00..42.18 rows=518 width=564)
(actual time=0.041..1.833 rows=5000 loops=1)
Planning time: 1.840 ms
Execution time: 6.611 ms
Ponieważ optymalizator bazy danych nie ma pojęcia, że musimy pobrać tylko 50 rekordów, zakłada, że należy przeskanować wszystkie 5000 wierszy. Jeśli zapytanie wymaga pobrania dużej liczby rekordów, koszt skanowania całej tabeli jest w rzeczywistości niższy niż w przypadku użycia indeksu, dlatego plan wykonania w ogóle nie będzie korzystał z indeksu.
Wniosek
Chociaż wygląda jak setMaxRows
to przenośne rozwiązanie ograniczające rozmiar ResultSet
, stronicowanie na poziomie SQL jest znacznie bardziej wydajne, jeśli optymalizator serwera bazy danych nie używa maxRows
JDBC właściwość.