Équivalent de LIMIT pour DB2

91

Comment faites-vous LIMITdans DB2 pour iSeries?

J'ai une table avec plus de 50 000 enregistrements et je souhaite renvoyer les enregistrements de 0 à 10 000 et les enregistrements de 10 000 à 20 000.

Je sais qu'en SQL vous écrivez LIMIT 0,10000à la fin de la requête pour 0 à 10 000 et LIMIT 10000,10000à la fin de la requête pour 10 000 à 20 000

Alors, comment cela se fait-il dans DB2? Quel est le code et la syntaxe? (un exemple de requête complet est apprécié)

elcool
la source
ROW_NUMBER () n'était implémenté que dans iSeries DB2 V5R4. Pour les versions précédentes, essayez d'utiliser RRN () qui est similaire.
Paul Morgan
RRN () est complètement différent de row_number ().
Brandon Peterson
N'a pas travaillé pour moi. Erreur Sytanx.
elcool
1
Essayez RRN (nom de fichier) qui donnera le numéro d'enregistrement relatif physique de la ligne. RRN ne sera pas séquentiel et peut sauter des nombres si des lignes ont été supprimées. Le RRN ne sera pas non plus séquentiel par clé, mais sera séquentiel en fonction de l'ajout si aucune suppression n'a eu lieu. Dans tous les cas, RRN sera unique pour une ligne et peut être utilisé pour sélectionner des sous-ensembles de la table.
Paul Morgan du
1
DB2 fournissant la prise en charge des mots-clés de limite à partir de DB2 9.7.2 selon programmationzen.com/2010/06/02/…
lakshman

Réponses:

139

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

Pour obtenir des plages, vous devez utiliser ROW_NUMBER()(depuis la v5r4) et l'utiliser dans la WHEREclause: (volé à partir d'ici: 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
la source
ouais, j'ai trouvé ça aussi, hehe. J'étais en train de modifier la question en même temps pour indiquer que je veux aussi les lignes du milieu.
elcool
2
Vous devez faire quelque chose comme ça avec ROW_NUMBER: justskins.com/forums/db2-select-how-to-123209.html
Joe
ROW_NUMBERn'est pas un mot clé valide. Mais merci pour le lien, ça m'a donné une idée et ça marche.
elcool
13

Développé cette méthode:

Vous BESOIN d'une table qui a une valeur unique qui peut être commandée.

Si vous voulez des lignes de 10 000 à 25 000 et que votre table comporte 40 000 lignes, vous devez d'abord obtenir le point de départ et le total des lignes:

int start = 40000 - 10000;

int total = 25000 - 10000;

Et puis passez-les par code à la requête:

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
la source
Notez que la 10000ème ligne est exclue du jeu de résultats, la première ligne est la 10001ème.
bleuté le
1
Solution intéressante. J'allais l'utiliser pour la compatibilité avec la base de données de test H2 ... Mais, malheureusement, cela fonctionne ~ 30 fois plus lentement que l'approche SELECT row_number () OVER (ORDER BY code).
manuna
9

La prise en charge de OFFSET et LIMIT a été récemment ajoutée à DB2 pour i 7.1 et 7.2. Vous avez besoin des niveaux de groupe DB PTF suivants pour obtenir cette prise en charge:

  • SF99702 niveau 9 pour IBM i 7.2
  • SF99701 niveau 38 pour IBM i 7.1

Voir ici pour plus d'informations: documentation OFFSET et LIMIT , DB2 for i Enhancement Wiki

Kevin Adler
la source
7

Voici la solution que j'ai trouvée:

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

En initialisant LASTVAL à 0 (ou «» pour un champ de texte), puis en le définissant sur la dernière valeur du jeu d'enregistrements le plus récent, cela fera défiler la table par blocs de N enregistrements.

Tom Barron
la source
(Je pensais au départ que vous définissiez la valeur dans le tableau, ce qui serait spectaculairement problématique sur un système concurrent) Oui, cela devrait fonctionner dans les cas où vous effectuez une lecture séquentielle dans le tableau, bien que vous ayez besoin d'une sorte de colonne tie-breaker dans le cas où Nest plus petit que le nombre de valeurs identiques dans la colonne (bien que cela soit vrai lors de l'utilisation ROW_NUMBER()aussi). Les valeurs initiales doivent également être choisies avec soin - ce 0sera évidemment problématique si la colonne contient une valeur négative . Des précautions seraient nécessaires avec les valeurs nulles. Ne fonctionnera pas si les pages sont ignorées.
Clockwork-Muse
Merci pour le commentaire. Je pense qu'il existe une hypothèse implicite selon laquelle le champ que nous utilisons pour contrôler la requête est unique et augmente de manière monotone. Je conviens que si ces hypothèses ne tiennent pas, cela ne fonctionnera pas pour visiter tous les enregistrements du tableau. Et, bien sûr, vous avez raison de dire que vous devez commencer par une LASTVAL qui a du sens. En général, je pense que vous voudriez commencer par tout ce qui est renvoyé par "sélectionnez MINIMUM (FIELD) from TABLE". Si le champ est indexé, la plupart des moteurs de base de données feront mieux que de lire la table entière de manière séquentielle.
Tom Barron
2

La solution de @ elcool est une idée intelligente, mais vous devez connaître le nombre total de lignes (qui peut même changer pendant que vous exécutez la requête!). Je propose donc une version modifiée, qui nécessite malheureusement 3 sous-requêtes au lieu 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

{last}doit être remplacé par le numéro de ligne du dernier enregistrement dont j'ai besoin et {length}doit être remplacé par le nombre de lignes dont j'ai besoin, calculé comme suit last row - first row + 1.

Par exemple, si je veux des lignes de 10 à 25 (au total 16 lignes), ce {last}sera 25 et {length}sera 25-10 + 1 = 16.

bleuâtre
la source
Je méprise ceux qui refusent de voter lorsqu'une autre personne prend le temps de répondre à leur question.
jp2code
1

Vous devez également considérer la clause OPTIMIZE FOR n ROWS. Plus de détails sur tout cela dans la documentation DB2 LUW dans la rubrique Directives pour la restriction des instructions SELECT :

  • La clause OPTIMIZE FOR déclare l'intention de récupérer uniquement un sous-ensemble du résultat ou de donner la priorité à la récupération uniquement des premières lignes. L'optimiseur peut alors choisir des plans d'accès qui minimisent le temps de réponse pour récupérer les premières lignes.
David Sky
la source
1

Essaye ça

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

Il existe 2 solutions pour paginer efficacement sur une table DB2:

1 - la technique utilisant la fonction row_number () et la clause OVER qui a été présentée sur un autre article ("SELECT row_number () OVER (ORDER BY ...)"). Sur certaines grandes tables, j'ai remarqué parfois une dégradation des performances.

2 - la technique utilisant un curseur à défilement. L'implémentation dépend de la langue utilisée. Cette technique semble plus robuste sur les grandes tables.

J'ai présenté les 2 techniques implémentées en PHP lors d'un séminaire l'année prochaine. La diapositive est disponible sur ce lien: http://gregphplab.com/serendipity/uploads/slides/DB2_PHP_Best_practices.pdf

Désolé mais ce document est uniquement en français.

gregphplab
la source
0

Theres ces options 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.  
Hector
la source