2016-07-27 4 views
1

J'ai une méthode qui lit les données en utilisant SqlDataReader et le rendement retourne un IEnumerable, .: par exemplePrendre (10) vs TOP 10 Avec SqlDataReader?

IEnumerable<string> LoadCustomers() 
{ 
using(SqlDataReader rdr = cmd.ExecuteReader()) 
{ 
    while (rdr.Read()) 
    { 
     yield return rdr.GetString(0); 
    } 
} 
} 

Maintenant, supposons que j'ai besoin que 10 nouveaux clients. Je pouvais faire

LoadCustomers.Take(10) 

ou passer 10 en tant que paramètre à sql et faire mon sql

SELECT TOP 10 FROM Customers ORDER BY CreationDate DESC 

Selon this post l'ensemble des résultats sont transmis depuis le serveur sql au client même si le datareader lit seulement quelques lignes (tant que la connexion est ouverte) - devrais-je éviter l'approche Take(10) parce que ces données supplémentaires sont transmises de toute façon au client ou il serait une optimisation prématurée pour l'éviter (parce que le code retour de rendement ferment la connexion après il a lu 10 lignes et alors la transmission de données s'arrêterait de toute façon)?

+2

'COMMANDER PAR CreationDate DESC'? – jarlh

+4

Ce ne serait pas une optimisation prématurée. Ne prenez dans la base de données que ce dont vous avez réellement besoin. Cela n'a aucun sens de sélectionner 10 000 clients alors que vous n'en avez besoin que de 10. –

+1

Vous interprétez mal ce que ce post dit. Le jeu de résultats entier n'est * pas * transmis au client si vous arrêtez de lire, bien que certaines lignes puissent déjà être mises en mémoire tampon. 'SqlDataReader' ne" lit pas "au-delà du réseau. La raison pour laquelle vous voulez toujours envoyer 'TOP (10)' au serveur de base de données dans la plupart des cas, et pourquoi ce n'est pas une optimisation prématurée, c'est parce que l'optimiseur peut produire un plan plus efficace s'il sait que vous voulez seulement 10 lignes. lire toute la table (si rien d'autre, la requête allouera moins de mémoire à l'avance). –

Répondre

2

Depuis que l'optimisation ou non "prématuré" est subjectif, je choisis d'interpréter cette question comme "utilise un DataReader et l'arrêt de la lecture après 10 lignes ont les mêmes caractéristiques de performance que l'utilisation de TOP(10) dans la requête? "

La réponse est non. Le passage au serveur TOP(10) permet à l'optimiseur de régler les lectures, les allocations de mémoire, les tampons d'E/S, la granularité et le parallélisme des verrous en sachant que la requête retournera (et dans ce cas également lira) au plus 10 lignes. En laissant de côté le TOP, cela signifie qu'il doit se préparer au cas où le client va lire toutes les lignes - que vous vous arrêtiez plus tôt ou plus tôt.

Il est faux que le serveur envoie des lignes que vous les lisiez ou non. Le fait de tirer des lignes avec un SqlDataReader est conceptuellement une opération ligne par ligne: lorsque vous exécutez Reader.MoveNext, vous récupérez la ligne suivante à partir du serveur et uniquement cette ligne. Mais dans l'intérêt de la performance, les lignes sont mises en mémoire tampon avant que vous les demandiez (à la fois sur le serveur et dans les tampons du réseau). Il est donc possible de se retrouver avec, disons, 100 lignes récupérées dans des tampons après votre premier appel .MoveNext, même si vous n'en avez lu que 10. En ce qui concerne les frais généraux, ce ne serait pas ma principale préoccupation parce que ces tampons ont finalement une taille fixe: le serveur ne va pas et tampon toutes les lignes d'un ensemble de résultats, peu importe combien ils sont (ce serait très inefficace en général). Si vous ne lisez que 10 lignes, si votre requête retournera 1 000 ou 1 000 000 lignes si elle a été complétée, cela n'aura aucune importance en termes de mise en mémoire tampon, mais principalement en termes de plan de requête. Néanmoins, cela s'ajoute aux frais généraux.

1

Vous pouvez également utiliser pagination Sauter (0) et Prendre (10) plus de flexibilité.

SQL SERVER 2012

SELECT name, 
     CreationDate   
    FROM customer 
ORDER BY 
     CreationDate  
OFFSET @skip ROWS 
FETCH NEXT @take ROWS ONLY; 

SQL 2005 à 2008

SET @take = (@skip + @take) 

;WITH customer_page_cte AS 
(SELECT 
     name, 
     CreationDate, 
     ROW_NUMBER() OVER (ORDER BY CreationDate desc) AS RowNumber 
FROM customer 
) 

SELECT name, 
     CreationDate 
    FROM customer_page_cte 
WHERE RowNumber > @skip AND RowNumber <= @take 

C# avec sql 2012 - utilisation procédure stockée pour la commande :)

var command = @"SELECT name, 
         CreationDate   
        FROM customer 
       ORDER BY 
         CreationDate  
       OFFSET @skip ROWS 
       FETCH NEXT @take ROWS ONLY;"; 

      using (var conn = new SqlConnection("Data Source=.;Initial Catalog=Stackoverflow;Integrated Security=True")) 
      { 
       conn.Open(); 
       using (var cmd = new SqlCommand(command, conn)) 
       { 
        cmd.Parameters.AddWithValue("skip", 0); 
        cmd.Parameters.AddWithValue("take", 10); 

        var reader = cmd.ExecuteReader(); 
        while (reader.Read()) 
        { 
         Console.WriteLine(reader.GetString(0)); 
        } 
       } 
      }