LÍMITE de nivel SQL
Para restringir el tamaño del conjunto de resultados de la consulta SQL, puede usar la sintaxis SQL:008:
SELECT title
FROM post
ORDER BY created_on DESC
OFFSET 50 ROWS
FETCH NEXT 50 ROWS ONLY
que funciona en Oracle 12, SQL Server 2012 o PostgreSQL 8.4 o versiones más recientes.
Para MySQL, puede usar las cláusulas LIMIT y OFFSET:
SELECT title
FROM post
ORDER BY created_on DESC
LIMIT 50
OFFSET 50
La ventaja de usar la paginación a nivel de SQL es que el plan de ejecución de la base de datos puede usar esta información.
Entonces, si tenemos un índice en el created_on
columna:
CREATE INDEX idx_post_created_on ON post (created_on DESC)
Y ejecutamos la siguiente consulta que usa el LIMIT
cláusula:
EXPLAIN ANALYZE
SELECT title
FROM post
ORDER BY created_on DESC
LIMIT 50
Podemos ver que el motor de la base de datos usa el índice ya que el optimizador sabe que solo se deben recuperar 50 registros:
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
Declaración JDBC maxRows
Según el setMaxRows
Javadoc
:
¡Eso no es muy tranquilizador!
Entonces, si ejecutamos la siguiente consulta en 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++;
}
}
Obtenemos el siguiente plan de ejecución en el registro de 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
Debido a que el optimizador de la base de datos no tiene idea de que necesitamos obtener solo 50 registros, asume que se deben escanear las 5000 filas. Si una consulta necesita obtener una gran cantidad de registros, el costo de una exploración de tabla completa es menor que si se usa un índice, por lo que el plan de ejecución no usará el índice en absoluto.
Conclusión
Aunque parece el setMaxRows
es una solución portátil para limitar el tamaño del ResultSet
, la paginación de nivel SQL es mucho más eficiente si el optimizador del servidor de la base de datos no usa JDBC maxRows
propiedad.