En utilisant Linq à SQL, comment trouver min et max d'une colonne dans une table?

Je veux trouver le moyen le plus rapide d'obtenir le min et le max d'une colonne dans une table avec un seul aller-retour Linq to SQL. Donc, je sais que cela fonctionnerait en deux aller-retour:

int min = MyTable.Min(row => row.FavoriteNumber);
int max = MyTable.Max(row => row.FavoriteNumber);

Je sais que je peux utiliser group mais je n'ai pas de clause group by, je veux agréger sur toute la table! Et je ne peux pas utiliser le .Min sans regroupement en premier. J'ai essayé ceci:

from row in MyTable 
group row by true into r 
select new { 
    min = r.Min(z => z.FavoriteNumber), 
    max = r.Max(z => z.FavoriteNumber) 
}

Mais cette clause de groupe folle semble stupide, et le SQL qu'elle fait est plus complexe qu'il ne doit l'être.

Affirmative, est-il façon de simplement obtenir le bon SQL?

EDIT: ces gars ont aussi échoué: Linq to SQL: comment agréger sans groupe par? ... supervision boiteux par les concepteurs LINQ s'il n'y a vraiment pas de réponse.

EDIT 2: j'ai regardé ma propre solution (avec la clause group BY constante absurde) dans L'analyse du plan D'exécution de SQL Server Management Studio, et il me semble qu'elle est identique au plan généré par:

SELECT MIN(FavoriteNumber), MAX(FavoriteNumber)
FROM MyTable

Donc, à moins que quelqu'un puisse trouver un réponse plus simple ou tout aussi bonne, je pense que je dois le marquer comme répondu par moi-même. Pensées?

26
demandé sur Community 2010-02-15 21:45:08

5 réponses

Comme indiqué dans la question, cette méthode semble générer du code SQL optimal, donc bien qu'elle semble un peu squirrely dans LINQ, elle devrait être optimale en termes de performances.

from row in MyTable  
group row by true into r  
select new {  
    min = r.Min(z => z.FavoriteNumber),  
    max = r.Max(z => z.FavoriteNumber)  
} 
27
répondu Scott Stafford 2010-02-22 22:15:46

Je n'ai pu trouver que celui-ci qui produit un sql un peu propre toujours pas vraiment efficace en comparant à select min (val), max (val) de la table:

var r =
  (from min in items.OrderBy(i => i.Value)
   from max in items.OrderByDescending(i => i.Value)
   select new {min, max}).First();

Le sql est

SELECT TOP (1)
    [t0].[Value],
    [t1].[Value] AS [Value2]
FROM
    [TestTable] AS [t0],
    [TestTable] AS [t1]
ORDER BY
    [t0].[Value],
    [t1].[Value] DESC

Il existe toujours une autre option pour utiliser une connexion unique pour les requêtes min et max (Voir plusieurs ensembles de résultats actifs (MARS))

Ou procédure stockée..

6
répondu dh. 2010-02-15 21:09:16

Je ne sais pas encore comment le traduire en C# (je travaille dessus)

C'est la version Haskell

minAndMax :: Ord a => [a] -> (a,a)
minAndMax [x]    = (x,x)
minAndMax (x:xs) = (min a x, max b x)
                   where (a,b) = minAndMax xs

La version C # devrait impliquer Aggregate un peu comment (je pense).

2
répondu Matt Ellen 2010-02-15 20:14:42

Une requête LINQ to SQL est une expression unique. Ainsi, si vous ne pouvez pas exprimer votre requête en une seule expression (ou si vous ne l'aimez pas une fois que vous le faites), vous devez regarder d'autres options.

Les procédures stockées, puisqu'elles peuvent avoir des instructions, vous permettent d'accomplir cela en un seul aller-retour. Vous aurez deux paramètres de sortie ou sélectionnez un jeu de résultats avec deux lignes. De toute façon, vous aurez besoin de code personnalisé pour lire le résultat de la procédure stockée.

(Je ne vois pas personnellement le nécessité d'éviter deux voyages aller-retour ici. Cela semble être une optimisation prématurée, d'autant plus que vous devrez probablement sauter à travers des cerceaux pour le faire fonctionner. Sans parler du temps que vous passerez à justifier cette décision et à expliquer la solution aux autres développeurs.)

Autrement dit: vous avez déjà répondu à votre propre question. "Je ne peux pas utiliser le .Min sans regroupement en premier", suivi de " cette clause de groupe folle semble stupide, et le SQL qu'elle fait est plus complexe qu'il ne doit l'être", sont des indices que la solution simple et facile à comprendre à deux va-et-vient est la meilleure expression de votre intention (sauf si vous écrivez SQL personnalisé).

1
répondu Bryan Watts 2010-02-15 19:12:27

Vous pouvez sélectionner toute la table et effectuer vos opérations min et max en mémoire:

var cache = // select *

var min = cache.Min(...);
var max = cache.Max(...);

En fonction de la taille de votre jeu de données, cela peut être le moyen de ne pas toucher votre base de données plus d'une fois.

0
répondu cllpse 2010-02-15 18:51:26