Equivalente de LIMIT para DB2

91

¿Cómo le va LIMITen DB2 para iSeries?

Tengo una tabla con más de 50,000 registros y quiero devolver registros de 0 a 10,000 y registros de 10,000 a 20,000.

Sé que en SQL escribe LIMIT 0,10000al final de la consulta de 0 a 10,000 y LIMIT 10000,10000al final de la consulta de 10000 a 20,000

Entonces, ¿cómo se hace esto en DB2? ¿Cuál es el código y la sintaxis? (se agradece el ejemplo de consulta completo)

elcool
fuente
ROW_NUMBER () solo se implementó en iSeries DB2 V5R4. Para versiones anteriores, intente usar RRN () que es similar.
Paul Morgan
RRN () es completamente diferente a row_number ().
Brandon Peterson
no funcionó para mí. Error de Sytanx.
elcool
1
Pruebe RRN (nombre de archivo) que le dará el número de registro físico relativo de la fila. RRN no será secuencial y puede omitir números si se han eliminado filas. RRN tampoco será secuencial por clave, pero será secuencial según la adición si no se han producido eliminaciones. En cualquier caso, RRN será único para una fila y se puede utilizar para seleccionar subconjuntos de la tabla.
Paul Morgan
1
DB2 proporciona soporte de palabras clave de límite de DB2 9.7.2 de acuerdo con programaciónzen.com
2010

Respuestas:

139

Usando FETCH FIRST [n] ROWS ONLY:

http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db29.doc.perf/db2z_fetchfirstnrows.htm

SELECT LASTNAME, FIRSTNAME, EMPNO, SALARY
  FROM EMP
  ORDER BY SALARY DESC
  FETCH FIRST 20 ROWS ONLY;

Para obtener rangos, tendría que usar ROW_NUMBER()(desde v5r4) y usar eso dentro de la WHEREcláusula: (robado de aquí: http://www.justskins.com/forums/db2-select-how-to-123209.html )

SELECT code, name, address
FROM ( 
  SELECT row_number() OVER ( ORDER BY code ) AS rid, code, name, address
  FROM contacts
  WHERE name LIKE '%Bob%' 
  ) AS t
WHERE t.rid BETWEEN 20 AND 25;
Joe
fuente
sí, yo también encontré esto, jeje. Estaba editando la pregunta al mismo tiempo para indicar que también quiero filas intermedias.
elcool
2
Tienes que hacer algo como esto con ROW_NUMBER: justskins.com/forums/db2-select-how-to-123209.html
Joe
ROW_NUMBERno es una palabra clave válida. Pero gracias por el enlace, me dio una idea y funciona.
elcool
13

Desarrolló este método:

NECESITA una tabla que tenga un valor único que se pueda pedir.

Si desea filas de 10,000 a 25,000 y su tabla tiene 40,000 filas, primero debe obtener el punto de partida y las filas totales:

int start = 40000 - 10000;

int total = 25000 - 10000;

Y luego pase estos por código a la consulta:

SELECT * FROM 
(SELECT * FROM schema.mytable 
ORDER BY userId DESC fetch first {start} rows only ) AS mini 
ORDER BY mini.userId ASC fetch first {total} rows only
elcool
fuente
Tenga en cuenta que la fila número 10000 se excluye del conjunto de resultados, la primera fila es la número 10001.
azulado
1
Interesante solución. Iba a usarlo para compatibilidad con la base de datos de prueba H2 ... Pero, lamentablemente, funciona ~ 30 veces más lento que el enfoque SELECT row_number () OVER (ORDER BY code).
manuna
9

Recientemente se agregó soporte para OFFSET y LIMIT a DB2 para i 7.1 y 7.2. Necesita los siguientes niveles de grupo DB PTF para obtener este soporte:

  • SF99702 nivel 9 para IBM i 7.2
  • SF99701 nivel 38 para IBM i 7.1

Consulte aquí para obtener más información: documentación de OFFSET y LIMIT , DB2 para i Enhancement Wiki

Kevin Adler
fuente
7

Aquí está la solución que se me ocurrió:

select FIELD from TABLE where FIELD > LASTVAL order by FIELD fetch first N rows only;

Al inicializar LASTVAL en 0 (o '' para un campo de texto), luego establecerlo en el último valor en el conjunto de registros más reciente, esto recorrerá la tabla en trozos de N registros.

Tom Barron
fuente
(Inicialmente pensé que estabas estableciendo el valor en la tabla, lo cual sería espectacularmente problemático en un sistema concurrente) Sí, esto debería funcionar en los casos en los que estás haciendo una lectura secuencial de la tabla, aunque necesitarías algún tipo de columna de desempate en el caso de que Nsea ​​menor que el número de valores idénticos en la columna (aunque esto también es cierto cuando se usa ROW_NUMBER()). Los valores iniciales también deben elegirse con cuidado; 0obviamente, será problemático si la columna contiene un valor negativo . Se necesitaría cuidado con los nulos. No funcionará si se saltan las páginas.
Clockwork-Muse
Gracias por el comentario. Creo que hay una suposición implícita de que el campo que estamos usando para controlar la consulta es único y aumenta monótonamente. Estoy de acuerdo en que si esas suposiciones no se cumplen, esto no funcionará para visitar todos los registros de la tabla. Y, por supuesto, tiene razón en que tendría que comenzar con un LASTVAL que tenga sentido. En general, creo que querrá comenzar con lo que devuelva "seleccione MÍNIMO (CAMPO) de TABLA". Si el campo está indexado, la mayoría de los motores de base de datos funcionarán mejor que leer toda la tabla secuencialmente.
Tom Barron
2

La solución de @elcool es una idea inteligente, pero necesita saber el número total de filas (¡que incluso puede cambiar mientras ejecuta la consulta!). Entonces propongo una versión modificada, que desafortunadamente necesita 3 subconsultas en lugar de 2:

select * from (
    select * from (
        select * from MYLIB.MYTABLE
        order by MYID asc 
        fetch first {last} rows only 
        ) I 
    order by MYID desc
    fetch first {length} rows only
    ) II
order by MYID asc

donde {last}debe reemplazarse con el número de fila del último registro que necesito y {length}debe reemplazarse con el número de filas que necesito, calculado como last row - first row + 1.

Por ejemplo, si quiero filas de 10 a 25 (en total 16 filas), {last}será 25 y {length}será 25-10 + 1 = 16.

azulado
fuente
Detesto a los que votan negativamente cuando otra persona se toma el tiempo para responder a su pregunta.
jp2code
1

También debe considerar la cláusula OPTIMIZE FOR n ROWS. Más detalles sobre todo esto en la documentación de DB2 LUW en el tema Directrices para restringir sentencias SELECT :

  • La cláusula OPTIMIZE FOR declara la intención de recuperar solo un subconjunto del resultado o dar prioridad a la recuperación solo de las primeras filas. El optimizador puede elegir planes de acceso que minimicen el tiempo de respuesta para recuperar las primeras filas.
David Sky
fuente
1

Prueba esto

SELECT * FROM
    (
        SELECT T.*, ROW_NUMBER() OVER() R FROM TABLE T
    )
    WHERE R BETWEEN 10000 AND 20000
Lucio Menci
fuente
0

Hay 2 soluciones para paginar de manera eficiente en una tabla DB2:

1 - la técnica que utiliza la función número_fila () y la cláusula OVER que se ha presentado en otra publicación ("SELECT número_fila () OVER (ORDER BY ...)"). En algunas mesas grandes, a veces noté una degradación de las actuaciones.

2 - la técnica que utiliza un cursor desplazable. La implementación depende del idioma utilizado. Esa técnica parece más sólida en mesas grandes.

Presenté las 2 técnicas implementadas en PHP durante un seminario el próximo año. La diapositiva está disponible en este enlace: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

Lo siento, pero este documento solo está en francés.

gregphplab
fuente
0

Hay estas opciones disponibles: -

DB2 has several strategies to cope with this problem.
You can use the "scrollable cursor" in feature.
In this case you can open a cursor and, instead of re-issuing a query you can FETCH forward and backward.
This works great if your application can hold state since it doesn't require DB2 to rerun the query every time.
You can use the ROW_NUMBER() OLAP function to number rows and then return the subset you want.
This is ANSI SQL 
You can use the ROWNUM pseudo columns which does the same as ROW_NUMBER() but is suitable if you have Oracle skills.
You can use LIMIT and OFFSET if you are more leaning to a mySQL or PostgreSQL dialect.  
Héctor
fuente