Pagination dans SQL Server


17

J'ai une très grande base de données, environ 100 Go. J'exécute la requête:

select * from <table_name>;

et je veux afficher uniquement les 100e à 200e rangées.

Je veux comprendre comment cela se produit en interne. La base de données récupère-t-elle tous les enregistrements du disque dans la mémoire et renvoie-t-elle les 100e à 400e lignes au client interrogateur? Ou existe-t-il un mécanisme, de sorte que seuls ces enregistrements (100e-200e) soient extraits de la base de données - en utilisant un mécanisme d'indexation comme les arbres B, etc.?

J'ai trouvé que cela est lié au concept de pagination, mais je n'ai pas pu trouver exactement comment cela se produit en interne au niveau de la base de données.

Réponses:


37

Dans la requête que vous avez publiée:

select * from <table_name>;

Les 100e à 200e lignes n'existent pas, car vous ne spécifiez pas de ORDER BY. L'ordre n'est pas garanti, sauf si vous incluez la commande par pour de nombreuses raisons intéressantes, mais ce n'est pas vraiment le point ici.

Donc, pour illustrer votre propos, utilisons un tableau - je vais utiliser le tableau Utilisateurs du vidage de données Stack Overflow et exécuter cette requête:

SELECT * FROM dbo.Users ORDER BY DisplayName;

Par défaut, il n'y a pas d'index dans le champ DisplayName, donc SQL Server doit analyser la table entière, puis la trier par DisplayName. Voici le plan d'exécution :

Analyse d'index en cluster avec un tri

Ce n'est pas joli - c'est beaucoup de travail, avec un coût de sous-arbre estimé à environ 30k. (Vous pouvez le voir en plaçant votre souris sur l'opérateur de sélection sur PasteThePlan.) Alors, que se passe-t-il si nous voulons uniquement les lignes 100-200? Nous pouvons utiliser cette syntaxe dans SQL Server 2012+:

SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;

Le plan d'exécution à ce sujet est également très laid:

Analyse d'index en cluster avec un tri et un sommet

SQL Server analyse toujours toute la table pour créer la liste triée juste pour vous donner vos lignes 100-200, et le coût est toujours d'environ 30k. Pire encore, toute cette liste sera reconstruite à chaque exécution de votre requête (car après tout, il se peut que quelqu'un ait changé de nom d'affichage).

Pour l'accélérer, nous pouvons créer un index non cluster sur DisplayName, qui est une copie de notre table, triée par ce champ spécifique:

CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);

Avec cet index, le plan d'exécution de notre requête effectue désormais une recherche d'index:

Recherche d'index et recherche de clé

La requête se termine instantanément et a un coût estimé de sous-arbre de seulement 0,66 (contre 30k).

En résumé, si vous organisez les données de manière à prendre en charge les requêtes que vous exécutez fréquemment, alors oui, SQL Server peut prendre des raccourcis pour accélérer vos requêtes. Si, par contre, vous n'avez que des tas ou des index clusterisés, vous êtes foutu.


"Par défaut, il n'y a pas d'index dans le champ DisplayName, donc SQL Server doit analyser la table entière, puis la trier par DisplayName." Excusez-moi s'il s'agit d'une question très basique - dans le cas que j'ai cité de votre réponse, lorsque vous dit "Scannez la table entière", cela signifie-t-il que toutes les données doivent être mises en mémoire et triées (ce qui ne ressemble pas à la bonne façon)?
AV94

D'après votre réponse, je comprends que si le champ est indexé, alors faire des requêtes comme - obtenir la 100e à la 200e ligne est très efficace car SQL recherche l'index (arbre B, etc.) et va directement à ce point (100e ligne). Pourriez-vous s'il vous plaît me dire si c'est bien compris?
AV94

@AnilVedala à propos de votre première question - oui, les données doivent être triées. Sinon, comment une base de données pourrait-elle accomplir cela avec une liste non triée?
Brent Ozar

1
@AnilVedala à propos de votre deuxième question - c'est là qu'intervient le dernier plan d'exécution que je vous ai donné. (Si vous demandez comment lire un plan d'exécution, prenez le livre Plans d'exécution de Grant Fritchey.)
Brent Ozar

15

Tout comme un ajout à la réponse de Brent lors de l'utilisation d'un index non couvrant pour éviter un tri, il existe un problème potentiel avec les numéros de page ultérieurs qui peuvent être vus en exécutant ce qui suit

SELECT * 
FROM dbo.Users 
ORDER BY DisplayName 
OFFSET 100000 ROWS 
FETCH NEXT 100 ROWS ONLY;

Le plan d'exécution montre que la recherche a été exécutée 100 100 fois même si toutes les lignes sauf 100 sont ensuite filtrées par l'opérateur TOP.

entrez la description de l'image ici

Cela peut être atténué en utilisant le modèle ci-dessous

WITH T
     AS (SELECT Id,
                DisplayName
         FROM   dbo.Users
         ORDER  BY DisplayName
        OFFSET 100000 ROWS 
        FETCH NEXT 100 ROWS ONLY
        )
SELECT U.*
FROM   dbo.Users U
       JOIN T
         ON U.Id = T.Id
ORDER  BY T.DisplayName 

Cela filtre tout sauf les 100 dernières lignes avant d' effectuer les recherches, ce qui peut avoir un impact significatif sur la vitesse pour les grandes valeurs de décalage.

entrez la description de l'image ici


3

Cela dépend vraiment de la façon dont vous implémentez la pagination dans votre requête, de la nature des données et de la façon dont votre système est configuré. Il est assez sûr de dire que SQL Server tentera de renvoyer vos données en utilisant ce qu'il estime être le moins d'effort possible. Si vous n'avez pas d'ordre de tri explicite, de filtrage, de regroupement ou de fenêtrage, SQL Server pourrait éventuellement optimiser le plan de requête de sorte qu'il puisse renvoyer uniquement les pages du disque contenant les données requises par votre requête - ou mieux encore, directement à partir du pool de tampons. Dès que vous commencez à modifier la requête pour inclure le tri, le regroupement, le fenêtrage et le filtrage, cela commence à se compliquer.

Il y a un très bon article sur les performances SQL ici qui explique en détail les différentes méthodes de pagination et comment elles affectent le plan de requête. Je recommanderais fortement de le lire, puis d'essayer certaines des différentes méthodes qu'ils indiquent et de voir quel plan de requête est choisi sur votre propre système.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.