Quelle est la meilleure façon d'obtenir la valeur Max à partir d'une requête LINQ qui peut ne renvoyer aucune ligne? Si je fais juste
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter).Max
J'obtiens une erreur lorsque la requête ne renvoie aucune ligne. je pourrais faire
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter _
Order By MyCounter Descending).FirstOrDefault
mais cela semble un peu obtus pour une demande aussi simple. Est-ce que je manque une meilleure façon de le faire?
MISE À JOUR: Voici l'histoire: j'essaie de récupérer le prochain compteur d'éligibilité à partir d'une table enfant (système hérité, ne me lancez pas ...). La première ligne d'éligibilité pour chaque patient est toujours 1, la seconde est 2, etc. (ce n'est évidemment pas la clé primaire de la table enfant). Donc, je sélectionne la valeur de compteur existante maximale pour un patient, puis j'y ajoute 1 pour créer une nouvelle ligne. Lorsqu'il n'y a aucune valeur enfant existante, j'ai besoin que la requête renvoie 0 (donc l'ajout de 1 me donnera une valeur de compteur de 1). Notez que je ne veux pas me fier au nombre brut de lignes enfants, au cas où l'application héritée introduirait des lacunes dans les valeurs de compteur (possible). Mon mal d'essayer de rendre la question trop générique.
la source
Max
d'unDateTime
.Max(x => (DateTime?)x.TimeStamp)
toujours le seul moyen ..J'ai juste eu un problème similaire, mais j'utilisais des méthodes d'extension LINQ sur une liste plutôt qu'une syntaxe de requête. Le casting pour une astuce Nullable fonctionne également ici:
la source
Sonne comme un cas pour
DefaultIfEmpty
(le code non testé suit):la source
var colCount = RowsEnumerable.Select(row => row.Cols.Count).DefaultIfEmpty().Max()
Pensez à ce que vous demandez!
Le maximum de {1, 2, 3, -1, -2, -3} est évidemment 3. Le maximum de {2} est évidemment 2. Mais quel est le maximum de l'ensemble vide {}? De toute évidence, c'est une question dénuée de sens. Le maximum de l'ensemble vide n'est tout simplement pas défini. Tenter d'obtenir une réponse est une erreur mathématique. Le maximum de tout ensemble doit lui-même être un élément de cet ensemble. L'ensemble vide n'a pas d'éléments, donc prétendre qu'un nombre particulier est le maximum de cet ensemble sans être dans cet ensemble est une contradiction mathématique.
Tout comme il est correct pour l'ordinateur de lancer une exception lorsque le programmeur lui demande de diviser par zéro, il est donc correct pour l'ordinateur de lancer une exception lorsque le programmeur lui demande de prendre le maximum de l'ensemble vide. Division par zéro, prendre le maximum de l'ensemble vide, agiter le spacklerorke et chevaucher la licorne volante jusqu'à Neverland sont tous dénués de sens, impossibles, indéfinis.
Maintenant, que voulez-vous réellement faire?
la source
Vous pouvez toujours ajouter
Double.MinValue
à la séquence. Cela garantirait qu'il y a au moins un élément etMax
ne le renverrait que s'il s'agit réellement du minimum. Pour déterminer quelle option est plus efficace (Concat
,FirstOrDefault
ouTake(1)
), vous devez effectuer l' étalonnage adéquat.la source
Si la liste contient des éléments (c'est-à-dire non vide), elle prendra le maximum du champ MyCounter, sinon retournera 0.
la source
Depuis .Net 3.5, vous pouvez utiliser DefaultIfEmpty () en passant la valeur par défaut comme argument. Quelque chose comme l'une des manières suivantes:
Le premier est autorisé lorsque vous interrogez une colonne NOT NULL et le second est la façon dont un l'a utilisé pour interroger une colonne NULLABLE. Si vous utilisez DefaultIfEmpty () sans arguments, la valeur par défaut sera celle définie pour le type de votre sortie, comme vous pouvez le voir dans le tableau des valeurs par défaut .
Le SELECT résultant ne sera pas si élégant mais il est acceptable.
J'espère que ça aide.
la source
Je pense que le problème est de savoir ce que vous voulez qu'il se passe lorsque la requête n'a pas de résultats. S'il s'agit d'un cas exceptionnel, j'envelopperais la requête dans un bloc try / catch et gérerais l'exception générée par la requête standard. S'il est acceptable que la requête ne renvoie aucun résultat, vous devez déterminer ce que vous voulez que le résultat soit dans ce cas. Il se peut que la réponse de @ David (ou quelque chose de similaire fonctionne). Autrement dit, si le MAX sera toujours positif, alors il peut être suffisant d'insérer une "mauvaise" valeur connue dans la liste qui ne sera sélectionnée que s'il n'y a pas de résultats. Généralement, je m'attendrais à ce qu'une requête qui récupère un maximum ait des données sur lesquelles travailler et j'irais sur la route try / catch, sinon vous êtes toujours obligé de vérifier si la valeur que vous avez obtenue est correcte ou non. JE'
la source
Une autre possibilité serait le regroupement, similaire à la façon dont vous pourriez l'aborder en SQL brut:
La seule chose est (de tester à nouveau dans LINQPad) le passage à la version VB LINQ donne des erreurs de syntaxe sur la clause de regroupement. Je suis sûr que l'équivalent conceptuel est assez facile à trouver, je ne sais tout simplement pas comment le refléter dans VB.
Le SQL généré serait quelque chose du genre:
Le SELECT imbriqué semble capricieux, comme l'exécution de la requête récupérerait toutes les lignes puis sélectionnerait celle qui correspond dans l'ensemble récupéré ... la question est de savoir si SQL Server optimise la requête en quelque chose de comparable à l'application de la clause where au SELECT interne. Je regarde ça maintenant ...
Je ne connais pas bien l'interprétation des plans d'exécution dans SQL Server, mais il semble que lorsque la clause WHERE se trouve sur le SELECT externe, le nombre de lignes réelles résultant de cette étape correspond à toutes les lignes de la table, par rapport uniquement aux lignes correspondantes lorsque la clause WHERE est sur le SELECT interne. Cela dit, il semble que seulement 1% du coût soit transféré à l'étape suivante lorsque toutes les lignes sont prises en compte, et de toute façon, une seule ligne revient jamais du serveur SQL, alors peut-être que ce n'est pas une si grande différence dans le grand schéma des choses .
la source
peu tard, mais j'avais le même souci ...
En reformulant votre code à partir du message d'origine, vous voulez le maximum de l'ensemble S défini par
Tenir compte de votre dernier commentaire
Je peux reformuler votre problème comme suit: Vous voulez le maximum de {0 + S}. Et il semble que la solution proposée avec concat soit sémantiquement la bonne :-)
la source
Pourquoi pas quelque chose de plus direct comme:
la source
Une différence intéressante qui semble à noter est que si FirstOrDefault et Take (1) génèrent le même SQL (selon LINQPad, de toute façon), FirstOrDefault renvoie une valeur - la valeur par défaut - lorsqu'il n'y a pas de lignes correspondantes et Take (1) renvoie aucun résultat ... au moins dans LINQPad.
la source
Juste pour que tout le monde sache que l'utilisation de Linq to Entities, les méthodes ci-dessus ne fonctionneront pas ...
Si vous essayez de faire quelque chose comme
Cela lèvera une exception:
Je suggérerais juste de faire
Et le
FirstOrDefault
renverra 0 si votre liste est vide.la source
la source
J'ai mis au point une
MaxOrDefault
méthode d'extension. Il n'y a pas grand-chose à faire mais sa présence dans Intellisense est un rappel utile queMax
sur une séquence vide provoquera une exception. En outre, la méthode permet de spécifier la valeur par défaut si nécessaire.la source
Pour Entity Framework et Linq to SQL, nous pouvons y parvenir en définissant une méthode d'extension qui modifie une méthode
Expression
passée àIQueryable<T>.Max(...)
:Usage:
La requête générée est identique, elle fonctionne comme un appel normal à une
IQueryable<T>.Max(...)
méthode, mais s'il n'y a pas d'enregistrements, elle renvoie une valeur par défaut de type T au lieu de lancer une exceptionla source
J'ai juste eu un problème similaire, mes tests unitaires ont réussi avec Max () mais ont échoué lorsqu'ils sont exécutés sur une base de données en direct.
Ma solution était de séparer la requête de la logique en cours d'exécution, et non de les joindre en une seule requête.
J'avais besoin d'une solution pour travailler dans des tests unitaires en utilisant des objets Linq (dans Linq-objects Max () fonctionne avec des valeurs nulles) et Linq-sql lors de l'exécution dans un environnement en direct.
(Je me moque du Select () dans mes tests)
Moins efficace? Probablement.
Dois-je m'en soucier, tant que mon application ne tombera pas la prochaine fois? Nan.
la source