Equivalente a LIMIT per DB2


91

Come ti trovi LIMITin DB2 per iSeries?

Ho una tabella con più di 50.000 record e desidero restituire i record da 0 a 10.000 e i record da 10.000 a 20.000.

So che in SQL scrivi LIMIT 0,10000alla fine della query da 0 a 10.000 e LIMIT 10000,10000alla fine della query da 10000 a 20.000

Allora, come si fa in DB2? Qual è il codice e la sintassi? (l'esempio di query completo è apprezzato)


ROW_NUMBER () è stato implementato solo in iSeries DB2 V5R4. Per le versioni precedenti provare a utilizzare RRN () che è simile.
Paul Morgan,

RRN () è completamente diverso da row_number ().
Brandon Peterson

Non ha funzionato per me. Errore di Sytanx.
elcool

1
Prova RRN (nome file) che fornirà il numero di record relativo fisico della riga. RRN non sarà sequenziale e può saltare i numeri se le righe sono state eliminate. Inoltre, RRN non sarà sequenziale per chiave ma sarà sequenziale in base all'aggiunta se non sono state effettuate eliminazioni. In ogni caso RRN sarà unico per una riga e potrà essere utilizzato per selezionare sottoinsiemi della tabella.
Paul Morgan,

1
DB2 fornisce il supporto per le parole chiave limit da DB2 9.7.2 secondo programmingzen.com/2010/06/02/…
lakshman

Risposte:


139

Utilizzando 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;

Per ottenere intervalli, dovresti usare ROW_NUMBER()(dalla v5r4) e usarlo all'interno della WHEREclausola: (rubato da qui: 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;

si, ho trovato anche questo, hehe. Stavo modificando la domanda allo stesso tempo per indicare che voglio anche le righe centrali.
elcool

2
Devi fare qualcosa del genere con ROW_NUMBER: justskins.com/forums/db2-select-how-to-123209.html
Joe

ROW_NUMBERnon è una parola chiave valida. Ma grazie per il collegamento, mi ha dato un'idea e funziona.
elcool

13

Sviluppato questo metodo:

HAI BISOGNO di una tabella che abbia un valore univoco che possa essere ordinato.

Se vuoi righe da 10.000 a 25.000 e la tua tabella ha 40.000 righe, prima devi ottenere il punto iniziale e le righe totali:

int start = 40000 - 10000;

int total = 25000 - 10000;

E poi passali per codice alla query:

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

Notare che la 10000a riga è esclusa dal gruppo di risultati, la prima riga è la 10001a.
bluastro

1
Soluzione interessante. Lo avrei usato per la compatibilità con il database di test H2 ... Ma, purtroppo, funziona ~ 30 volte più lentamente dell'approccio SELECT row_number () OVER (ORDER BY code).
manuna

9

Il supporto per OFFSET e LIMIT è stato recentemente aggiunto a DB2 per i 7.1 e 7.2. Per ottenere questo supporto sono necessari i seguenti livelli di gruppo di PTF DB:

  • SF99702 livello 9 per IBM i 7.2
  • SF99701 livello 38 per IBM i 7.1

Vedere qui per ulteriori informazioni: documentazione OFFSET e LIMIT , Wiki di miglioramento di DB2 per i


7

Ecco la soluzione che ho trovato:

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

Inizializzando LASTVAL su 0 (o "" per un campo di testo), quindi impostandolo sull'ultimo valore nel set di record più recente, questa operazione passerà attraverso la tabella in blocchi di N record.


(Inizialmente pensavo che stessi impostando il valore nella tabella, il che sarebbe straordinariamente problematico su un sistema concorrente) Sì, questo dovrebbe funzionare nei casi in cui stai eseguendo una lettura sequenziale attraverso la tabella, sebbene avresti bisogno di una sorta di colonna tie-breaker nel caso in cui Nsia inferiore al numero di valori identici nella colonna (sebbene questo sia vero anche quando si utilizza ROW_NUMBER()). Anche i valori iniziali devono essere scelti con cura - 0sarà ovviamente problematico se la colonna contiene un valore negativo . Sarebbe necessaria la cura con i valori nulli. Non funzionerà se le pagine vengono saltate.
Clockwork-Muse

Grazie per il commento. Penso che ci sia un presupposto implicito che il campo che stiamo utilizzando per controllare la query sia unico e in aumento monotono. Sono d'accordo che se questi presupposti non sono validi, non funzionerà per visitare tutti i record nella tabella. E, naturalmente, hai ragione sul fatto che dovresti iniziare con un LASTVAL che ha senso. In generale, penso che dovresti iniziare con tutto ciò che viene restituito da "seleziona MINIMUM (FIELD) from TABLE". Se il campo è indicizzato, la maggior parte dei motori di database farà di meglio che leggere l'intera tabella in sequenza.
Tom Barron

2

La soluzione di @ elcool è un'idea intelligente, ma è necessario conoscere il numero totale di righe (che può anche cambiare durante l'esecuzione della query!). Quindi propongo una versione modificata, che purtroppo necessita di 3 sottoquery invece di 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

dove {last}dovrebbe essere sostituito con il numero di riga dell'ultimo record di cui ho bisogno e {length}dovrebbe essere sostituito con il numero di righe di cui ho bisogno, calcolato come last row - first row + 1.

Ad esempio, se voglio righe da 10 a 25 (in totale 16 righe), {last}sarà 25 e {length}sarà 25-10 + 1 = 16.


Disprezzo coloro che danno un voto negativo quando un'altra persona prende tempo per rispondere alla loro domanda.
jp2code

1

Dovresti anche considerare la clausola OPTIMIZE FOR n ROWS. Maggiori dettagli su tutto questo nella documentazione di DB2 LUW nell'argomento Linee guida per la limitazione delle istruzioni SELECT :

  • La clausola OPTIMIZE FOR dichiara l'intento di recuperare solo un sottoinsieme del risultato o di dare priorità al recupero solo delle prime righe. L'ottimizzatore può quindi scegliere piani di accesso che riducano al minimo il tempo di risposta per il recupero delle prime righe.

1

Prova questo

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

0

Esistono 2 soluzioni per impaginare in modo efficiente su una tabella DB2:

1 - la tecnica che utilizza la funzione row_number () e la clausola OVER che è stata presentata in un altro post ("SELECT row_number () OVER (ORDER BY ...)"). Su alcuni tavoli grandi, ho notato a volte un degrado delle prestazioni.

2 - la tecnica che utilizza un cursore scorrevole. L'implementazione dipende dalla lingua utilizzata. Questa tecnica sembra più robusta sui tavoli grandi.

Ho presentato le 2 tecniche implementate in PHP durante un seminario del prossimo anno. La diapositiva è disponibile a questo link: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

Siamo spiacenti ma questo documento è solo in francese.


0

Ci sono queste opzioni disponibili: -

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.  
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.