SQL pourquoi est-SELECT COUNT(*) , MIN(col), MAX(col), plus rapide, puis SÉLECTIONNEZ MIN(col), MAX(col)

Nous voyons une énorme différence entre ces requêtes.

La requête lente

SELECT MIN(col) AS Firstdate, MAX(col) AS Lastdate 
FROM table WHERE status = 'OK' AND fk = 4193

La Table 'table'. Nombre de Scan 2, lectures logiques 2458969, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.

D'Exécution de SQL Server Fois: temps CPU = 1966 ms, temps écoulé = 1955 ms.

Le rapide de la requête

SELECT count(*), MIN(col) AS Firstdate, MAX(col) AS Lastdate 
FROM table WHERE status = 'OK' AND fk = 4193

La Table 'table'. Analyse nombre 1, lectures logiques 5803, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.

D'Exécution de SQL Server Fois: temps CPU = 0 ms, le temps écoulé = 9 ms.

Question

Quelle est la raison, entre l'énorme différence de performances entre les requêtes?

Mise à jour
Une petite mise à jour basé sur des questions étant donné que les commentaires:

L'ordre de l'exécution ou de l'exécution répétée ne change en rien les performances de sage.
Il n'y a pas d'autres paramètres utilisés et le (test)de la base de données n'est pas faire autre chose pendant l'exécution.

Requête lente

|--Nested Loops(Inner Join)
 |--Stream Aggregate(DEFINE:([Expr1003]=MIN([DBTest].[dbo].[table].[startdate])))
   |    |--Top(TOP EXPRESSION:((1)))
   |         |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1008]) WITH ORDERED PREFETCH)
   |              |--Index Scan(OBJECT:([DBTest].[dbo].[table].[startdate]), ORDERED FORWARD)
   |              |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]),  WHERE:([DBTest].[dbo].[table].[FK]=(5806) AND [DBTest].[dbo].[table].[status]<>'A') LOOKUP ORDERED FORWARD)
   |--Stream Aggregate(DEFINE:([Expr1004]=MAX([DBTest].[dbo].[table].[startdate])))
        |--Top(TOP EXPRESSION:((1)))
             |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1009]) WITH ORDERED PREFETCH)
                  |--Index Scan(OBJECT:([DBTest].[dbo].[table].[startdate]), ORDERED BACKWARD)
                  |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]),  WHERE:([DBTest].[dbo].[table].[FK]=(5806) AND [DBTest].[dbo].[table].[status]<>'A') LOOKUP ORDERED FORWARD)

Rapide de la requête

 |--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1012],0)))
   |--Stream Aggregate(DEFINE:([Expr1012]=Count(*), [Expr1004]=MIN([DBTest].[dbo].[table].[startdate]), [Expr1005]=MAX([DBTest].[dbo].[table].[startdate])))
        |--Nested Loops(Inner Join, OUTER REFERENCES:([DBTest].[dbo].[table].[id], [Expr1011]) WITH UNORDERED PREFETCH)
             |--Index Seek(OBJECT:([DBTest].[dbo].[table].[FK]), SEEK:([DBTest].[dbo].[table].[FK]=(5806)) ORDERED FORWARD)
             |--Clustered Index Seek(OBJECT:([DBTest].[dbo].[table].[PK_table]), SEEK:([DBTest].[dbo].[table].[id]=[DBTest].[dbo].[table].[id]),  WHERE:([DBTest].[dbo].[table].[status]<'A' OR [DBTest].[dbo].[table].[status]>'A') LOOKUP ORDERED FORWARD)

SQL pourquoi est-SELECT COUNT(*) , MIN(col), MAX(col), plus rapide, puis SÉLECTIONNEZ MIN(col), MAX(col)

Réponse

La réponse donnée ci-dessous par Martin Smith semble expliquer le problème. La super version courte est que le MS-SQL de la requête-analyser, à tort, utilise un plan de requête dans la requête lente qui provoque une complète analyse de la table.

L'ajout d'un Count(*), l'indicateur de requête avec(FORCESCAN) ou un indice combiné sur la date de début,FK et le statut des colonnes résout le problème de performances.

  • si vous exécutez le 1er requête après la 2ème requête de nouveau?
  • Peut-être parce que lorsque vous utilisez un count(*) vous n'avez pas de vérifier tous les dossiers pour fk=4193?
  • Êtes-vous de l'exécution de ces l'un après l'autre? Si oui: qu'advient-il si vous mettez DBCC DROPCLEANBUFFERS et DBCC FREEPROCCACHE avant que les deux requêtes? Qu'advient-il si vous changez la séquence - exécuter la requête fast en premier, puis le lent?
  • Smith: je pensais à 1. Une mise à jour des stats 2. Reproducability
  • Votre plan ne correspond pas à la requête. -1 pour la trompeuse nous.
  • Gbn, pouvez-vous expliquer de quoi de différent? Les deux requêtes sont juste lavés et débarrassés de