Comment sélectionner les premiers "N" enregistrements d'une base de données contenant un million d'enregistrements?

StackOverflow https://stackoverflow.com/questions/1410048

Question

J'ai une base de données Oracle remplie de millions d'enregistrements. J'essaie d'écrire une requête SQL qui renvoie le premier 'N & Quot; enregistrements triés (par exemple 100 enregistrements) de la base de données en fonction de certaines conditions.

SELECT * 
FROM myTable 
Where SIZE > 2000 
ORDER BY NAME DESC

Sélectionnez ensuite par programmation les N premiers enregistrements.

Le problème avec cette approche est:

  • La requête aboutit à un demi million enregistrements et " ORDER BY NAME " causes tous les enregistrements à trier sur NOM dans l'ordre décroissant. Ce tri prend beaucoup de temps. (près de 30 à 40 secondes. Si j'oublie ORDER BY, cela ne prend qu'une seconde).
  • Après le genre qui m'intéresse seulement les premiers N (100) enregistrements. Le tri des enregistrements complets n'est donc pas utile.

Mes questions sont les suivantes:

  1. Est-il possible de spécifier le 'N' dans interroger lui-même? (de sorte que le tri s’applique uniquement à N enregistrements et que la requête devient plus rapide).
  2. Tout meilleur moyen en SQL d'améliorer la requête pour trier seulement N éléments et retour rapide le temps.
Était-ce utile?

La solution

Si votre but est de trouver 100 lignes aléatoires et de les trier ensuite, alors La solution de Lasse est correcte. Si, comme je le pense, vous souhaitez que les 100 premières lignes soient triées par nom tout en ignorant les autres, vous devez créer une requête comme celle-ci:

SELECT * 
  FROM (SELECT * 
          FROM myTable 
         WHERE SIZE > 2000 ORDER BY NAME DESC) 
 WHERE ROWNUM <= 100

L’optimiseur comprendra qu’il s’agit d’une requête TOP-N et pourra utiliser un index sur NAME. Il n'aura pas à trier l'ensemble des résultats, il commencera simplement à la fin de l'index, le lira à l'envers et s'arrêtera après 100 lignes.

Vous pouvez également ajouter un indice à votre requête d'origine pour permettre à l'optimiseur de comprendre que vous n'êtes intéressé que par les premières lignes. Cela générera probablement un chemin d'accès similaire:

SELECT /*+ FIRST_ROWS*/* FROM myTable WHERE SIZE > 2000 ORDER BY NAME DESC

Modifier: l'ajout de AND rownum <= 100 à la requête ne fonctionnera pas, car dans Oracle, rownum est attribué au avant : c'est pourquoi vous devez utiliser une sous-requête. Sans la sous-requête, Oracle sélectionnera 100 lignes aléatoires, puis les triera.

Autres conseils

Ce montre comment sélectionner les N premiers rangs en fonction de votre version d'Oracle.

  

À partir d’Oracle 9i, le RANK () et   Les fonctions DENSE_RANK () peuvent être utilisées pour   Déterminez les rangées TOP N. Exemples:

     

Obtenez le top 10 des employés en fonction de   leur salaire

     

SELECT ename, sal FROM (SELECT   ename, sal, RANK () OVER (COMMANDER PAR sal   DESC) sal_rank              DE emp) WHERE sal_rank & Lt; = 10;

     

Sélectionnez les employés qui se classent parmi les 10 meilleurs   salaires

     

SELECT ename, sal FROM (SELECT   ename, sal, DENSE_RANK () OVER (ORDER   BY sal DESC) sal_dense_rank              FROM emp) WHERE sal_dense_rank & Lt; = 10;

La différence entre les deux est expliquée ici

Ajouter ceci:

 AND rownum <= 100

à votre clause WHERE.

Cependant, cela ne fera pas ce que vous demandez.

Si vous souhaitez sélectionner 100 lignes aléatoires, les trier, puis les renvoyer, vous devez d'abord formuler une requête sans ORDER BY, puis limiter celle-ci à 100 lignes, puis sélectionner et trier.

Cela pourrait fonctionner, mais je n'ai malheureusement pas de serveur Oracle disponible pour tester:

SELECT *
FROM (
    SELECT *
    FROM myTable
    WHERE SIZE > 2000
      AND rownum <= 100
    ) x
ORDER BY NAME DESC

Mais notez le " random " une partie, vous dites & "donnez-moi 100 lignes avec SIZE > En 2000, je me fiche de savoir quelle <100>.

Est-ce vraiment ce que vous voulez?

Et non, vous n'obtiendrez pas de résultat aléatoire, car cela changera à chaque fois que vous interrogez le serveur, mais vous êtes à la merci de l'optimiseur de requêtes. Si la charge de données et les statistiques d'index de cette table changent avec le temps, vous obtiendrez peut-être des données différentes de celles de la requête précédente.

Votre problème est que le tri est effectué chaque fois que la requête est exécutée. Vous pouvez éliminer l’opération de tri en utilisant un index (l’optimiseur peut utiliser un index pour éliminer une opération de tri) si la colonne triée est déclarée NON NULL.

(Si la colonne est nullable, cela est toujours possible, soit en (a) ajoutant un prédicat NOT NULL à la requête, soit (b) en ajoutant un index basé sur une fonction et en modifiant la clause ORDER BY en conséquence).

Juste pour référence, dans Oracle 12c, cette tâche peut être effectuée à l'aide de la clause FETCH. Vous pouvez voir ici pour des exemples et des liens de référence supplémentaires sur ce sujet.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top