2010-01-05 6 views
7

Je reçois un comportement de plan d'exécution bizarre à partir de SQL Server (2005).sql server: Nombre estimé de lignes est loin

TableName: LOG
... contient environ 1000 lignes

  • ID int
  • Nom varchar (50)

Query:

SELECT * 
    FROM (SELECT ROW_NUMBER() OVER (ORDER BY ID DESC) as Row, 
       ID, Name 
      FROM Log) AS LogWithRowNumbers 
WHERE Row >= 1 
    AND Row <= 2 

Il estime le num Un nombre de rangées est retourné comme 9 (Bien que ce soit un 2 évident ou moins).
En outre, la suppression « et la ligne < = 2 » va augmenter le temps d'exécution d'environ * 5. (« et rangée < = 2 » et « et la ligne < = 9999999999999 » se comportent de la même)

J'ai statistiques mises à jour. Mais encore, ce comportement est étrange. L'ajout de la ligne < 99999999999 accélèrera l'exécution de la requête? Pourquoi ?

+0

Allez les gars, il est facilement reproductible. Où sont ces génies DBA quand vous en avez besoin ?? – Faruz

+0

@Faruz: Je ne suis pas un DBA. Mais la requête est-elle plus rapide lorsque vous avez un seul critère sans ET/OU (c'est-à-dire ROW = 1). Comment fonctionne-t-il quand 'ROW = 1 OR ROW = 2' par opposition à'> = 'et' <= '. Crée-t-il un index pour la table 'Log' en interne? – shahkalpesh

+0

Ligne = 1 ou ligne = 2 estimé 58 lignes renvoyées ... Il ne crée pas d'index interne mais utilise l'index PK (ID). – Faruz

Répondre

6

Je ne suis pas un expert sur le processus d'optimisation interne des requêtes/requêtes de SQL Server, mais voici mes 2 pence (ou cents si vous préférez).

Je crois que c'est dû à l'utilisation de la valeur ROW_NUMBER() dans la clause WHERE. À titre d'exemple, j'ai créé un exemple de tableau, rempli avec 1000 lignes de l'ID 1 à 1000 (ID comme une clé primaire) comme vous l'avez dit.

Si vous prenez le ROW_NUMBER(), et faire la requête en fonction de la colonne ID comme ceci:

Select * FROM 
(
SELECT ID, Name 
FROM Log 
) 
as LogWithRowNumbers 
WHERE ID>=1 and ID<=2 

Ensuite, il montre correctement le nombre de lignes comme étant 2 - comme prévu vraiment.

Maintenant, en travaillant en arrière, ajouter dans la ROW_NUMBER à SELECT interne mais laisser la clause where-est comme ci-dessous:

Select * FROM 
(
SELECT ROW_NUMBER() OVER (ORDER BY ID DESC) AS RowNo, 
ID, Name 
FROM Log 
) 
as LogWithRowNumbers 
WHERE ID>=1 AND ID <=2 

Cela montre encore le nombre de lignes correctes, 2.

Enfin , définissez la clause WHERE pour utiliser le RowNo comme la colonne filtrée au lieu de l'ID, c'est lorsque le nombre de lignes estimé saute à 9.

Par conséquent, je crois que c'est l'utilisation de la fonction ROW_NUMBER(), étant filtré dans la clause WHERE qui est le c ause. Donc j'imagine que c'est parce qu'il y a évidemment des statistiques meilleures/plus précises disponibles sur les colonnes de table réelles que sur cette valeur produite par la fonction. J'espère que cela fournit au moins un bon point de départ, j'espère que cela sera utile!

2

AdaTheDev est correct. La raison pour laquelle vous voyez ce comportement est que SQL Server doit calculer les numéros de ligne de la table avant de pouvoir les utiliser dans la clause where.

Cela devrait être un moyen plus efficace d'obtenir les mêmes résultats:

SELECT TOP(2) ROW_NUMBER() OVER (ORDER BY ID DESC) as Row, 
ID, Name 
FROM Log 
ORDER BY ID DESC 
+0

Je comprends ce que vous dites, seulement c'est un exemple d'une requête que je fais. Je récupère généralement les lignes 1-10, 10-20 etc. Le problème est, je dois le faire sans le "Row <999999" et cela coûte plus – Faruz

Questions connexes