ОГРАНИЧЕНИЕ на SQL ниво
За да ограничите размера на набора от резултати от SQL заявката, можете да използвате синтаксиса SQL:008:
SELECT title
FROM post
ORDER BY created_on DESC
OFFSET 50 ROWS
FETCH NEXT 50 ROWS ONLY
който работи на Oracle 12, SQL Server 2012 или PostgreSQL 8.4 или по-нови версии.
За MySQL можете да използвате клаузите LIMIT и OFFSET:
SELECT title
FROM post
ORDER BY created_on DESC
LIMIT 50
OFFSET 50
Предимството на използването на пагинация на ниво SQL е, че планът за изпълнение на базата данни може да използва тази информация.
Така че, ако имаме индекс на created_on
колона:
CREATE INDEX idx_post_created_on ON post (created_on DESC)
И ние изпълняваме следната заявка, която използва LIMIT
клауза:
EXPLAIN ANALYZE
SELECT title
FROM post
ORDER BY created_on DESC
LIMIT 50
Можем да видим, че машината на базата данни използва индекса, тъй като оптимизаторът знае, че трябва да бъдат извлечени само 50 записа:
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
JDBC израз maxRows
Според setMaxRows
Javadoc
:
Това не е много успокояващо!
И така, ако изпълним следната заявка на 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++;
}
}
Получаваме следния план за изпълнение в регистрационния файл на PostgreSQL:
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
Тъй като оптимизаторът на базата данни няма представа, че трябва да извлечем само 50 записа, той приема, че всичките 5000 реда трябва да бъдат сканирани. Ако една заявка трябва да извлече голям брой записи, цената на сканиране на пълна таблица всъщност е по-ниска, отколкото ако се използва индекс, следователно планът за изпълнение изобщо няма да използва индекса.
Заключение
Въпреки че изглежда като setMaxRows
е преносимо решение за ограничаване на размера на ResultSet
, странирането на ниво SQL е много по-ефективно, ако оптимизаторът на сървъра на база данни не използва JDBC maxRows
собственост.