2016-06-10 1 views
2

J'utilise le même morceau de code de pagination sql pendant des années .. et je ne l'ai maintenant remarqué cette bizarrerie .. Je pense qu'il est assez intéressant, et nous devrions en discuter . Voici donc la norme passe-partout en page:pagination sql quand « triez par » non-déterministe

SELECT a.* 
    FROM (SELECT b.*, 
       rownum b_rownum 
      FROM (SELECT c.* 
        FROM some_table c 
       ORDER BY some_column) b 
     WHERE rownum <= <<upper limit>>) a 
WHERE b_rownum >= <<lower limit>>` 

qui fonctionne fantastique si some_column est séquentiel.

mais j'exposer cela et permettant à l'utilisateur de trier sur une colonne, et si elles arrivent à choisir some_column qui est plein des mêmes valeurs - la pagination « casse ».

c'est-à-dire la requête renverra la même page de données exactes de la ligne après page. Et en y réfléchissant, pourquoi pas. il sélectionne probablement simplement les lignes les plus rapides ou tout ce qui passe le filtre.

donc par exemple, ces sql renvoient exactement les mêmes données

select * 
from (select a.*, ROWNUM rnum 
     from (select * from xsd order by PREFIX asc) a 
     where ROWNUM <= 30 
    ) 
where rnum >= 20; 


select * 
from (select a.*, ROWNUM rnum 
     from (select * from xsd order by PREFIX asc) a 
     where ROWNUM <= 40 
    ) 
where rnum >= 30; 

surtout, je pensais juste que était propre & je ne vois pas beaucoup de ce côté-effet dans d'autres postes.

Et aussi, je me suis demandé ce que je pouvais faire à ce sujet .. et si d'autre stratégie ajouterait le temps de traitement.

SOLUTION de Gordon, Alex

ajouter juste le rowid comme ordre final par défaut par. ces SQL sont maintenant différents. Je ne l'ai pas remarqué de changement dans le temps de réponse

select * 
from (select a.*, ROWNUM rnum 
     from (select * from xsd order by PREFIX asc, rowid) a 
     where ROWNUM <= 30 
    ) 
where rnum >= 20; 


select * 
from (select a.*, ROWNUM rnum 
     from (select * from xsd order by PREFIX asc, rowid) a 
     where ROWNUM <= 40 
    ) 
where rnum >= 30; 

une autre victoire pour stackoverflow, gentelmen remercie, heureux tout le monde codage

+0

Tag les DBMS utilisés. Ajoutez quelques exemples de données de table et ancien vs nouveau résultat. – jarlh

+0

J'ai ajouté une balise oracle, oui, je peux facilement fournir des timings avant et après. la seule chose à savoir sur les données est qu'il y a des milliers de 'PREFIX' avec la même valeur. –

Répondre

5

tables SQL représentent des ensembles non ordonnés. Par conséquent, les tris SQL ne sont pas stable. C'est-à-dire que les lignes ayant la même valeur de clé peuvent être dans n'importe quel ordre - et sur plusieurs passages, l'ordre peut changer. Il n'y a pas d'ordre "sous-jacent" pour les lignes.

La solution est d'ajouter un identifiant unique à la fin de la order by. Ce serait toujours la dernière touche:

select * 
from (select a.*, ROWNUM as rnum 
     from (select * from xsd order by PREFIX asc, id) a 
     where ROWNUM <= 40 
    ) 
where rnum >= 30; 

Si oui ou non cela affecte la performance dépend si les index peuvent être utilisés pour le tri. Si aucun index n'est utilisé, l'effet devrait être très mineur. Si l'ajout de la clé supplémentaire empêche l'utilisation d'un index, l'impact est beaucoup plus important.

+0

hmm, donc je devrais juste faire ma propre colonne 'id' et l'utiliser comme 'hidden by' par défaut? peut-être que je pourrais faire quelque chose avec une séquence, comme 'insérer dans emp (empno) valeurs (mySeq.nextVal);' –

+1

@SteveRenyolds - si vous n'avez pas déjà une clé unique (même si vous avez impliqué certaines des colonnes sont uniques?) peut-être pourriez-vous utiliser rowid, plutôt que d'ajouter une colonne fictive supplémentaire. Ce n'est pas entièrement stable, mais cela pourrait bien fonctionner ici. Si vous interrogez une table plutôt qu'une vue, bien sûr. –