Was ist der beste Weg, um den Max-Wert aus einer LINQ-Abfrage zu erhalten, die möglicherweise keine Zeilen zurückgibt? Wenn ich es nur tue
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter).Max
Ich erhalte eine Fehlermeldung, wenn die Abfrage keine Zeilen zurückgibt. ich könnte
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter _
Order By MyCounter Descending).FirstOrDefault
aber das fühlt sich für eine so einfache Anfrage etwas stumpf an. Vermisse ich einen besseren Weg, es zu tun?
UPDATE: Hier ist die Hintergrundgeschichte: Ich versuche, den nächsten Berechtigungszähler von einer untergeordneten Tabelle abzurufen (Legacy-System, lass mich nicht anfangen ...). Die erste Berechtigungszeile für jeden Patienten ist immer 1, die zweite 2 usw. (dies ist offensichtlich nicht der Primärschlüssel der untergeordneten Tabelle). Also wähle ich den maximal vorhandenen Zählerwert für einen Patienten aus und füge dann 1 hinzu, um eine neue Zeile zu erstellen. Wenn keine untergeordneten Werte vorhanden sind, muss die Abfrage 0 zurückgeben (wenn Sie also 1 hinzufügen, erhalten Sie einen Zählerwert von 1). Beachten Sie, dass ich mich nicht auf die rohe Anzahl der untergeordneten Zeilen verlassen möchte, falls die Legacy-App Lücken in den Zählerwerten einführt (möglich). Mein schlechtes für den Versuch, die Frage zu allgemein zu machen.
quelle
Max
von aDateTime
.Max(x => (DateTime?)x.TimeStamp)
immer noch der einzige Weg ..Ich hatte gerade ein ähnliches Problem, aber ich verwendete LINQ-Erweiterungsmethoden für eine Liste anstelle der Abfragesyntax. Das Casting zu einem Nullable-Trick funktioniert auch dort:
quelle
Klingt nach einem Fall für
DefaultIfEmpty
(ungetesteter Code folgt):quelle
var colCount = RowsEnumerable.Select(row => row.Cols.Count).DefaultIfEmpty().Max()
Überlegen Sie, was Sie fragen!
Das Maximum von {1, 2, 3, -1, -2, -3} ist offensichtlich 3. Das Maximum von {2} ist offensichtlich 2. Aber was ist das Maximum der leeren Menge {}? Das ist natürlich eine bedeutungslose Frage. Das Maximum des leeren Satzes ist einfach nicht definiert. Der Versuch, eine Antwort zu erhalten, ist ein mathematischer Fehler. Das Maximum einer Menge muss selbst ein Element in dieser Menge sein. Die leere Menge enthält keine Elemente. Die Behauptung, dass eine bestimmte Zahl das Maximum dieser Menge ist, ohne in dieser Menge zu sein, ist ein mathematischer Widerspruch.
So wie es ein korrektes Verhalten für den Computer ist, eine Ausnahme auszulösen, wenn der Programmierer ihn auffordert, durch Null zu teilen, so ist es ein korrektes Verhalten für den Computer, eine Ausnahme auszulösen, wenn der Programmierer ihn auffordert, das Maximum des leeren Satzes zu nehmen. Die Division durch Null, das Maximum des leeren Satzes, das Wackeln der Spacklerorke und das Reiten des fliegenden Einhorns nach Neverland sind alle bedeutungslos, unmöglich und undefiniert.
Was möchten Sie nun eigentlich tun?
quelle
Sie können
Double.MinValue
die Sequenz jederzeit ergänzen . Dies würde sicherstellen, dass mindestens ein Element vorhanden ist, undMax
es nur zurückgeben, wenn es tatsächlich das Minimum ist. Um festzustellen , welche Option effizienter ist (Concat
,FirstOrDefault
oderTake(1)
), sollten Sie eine angemessene Benchmarking durchzuführen.quelle
Wenn die Liste Elemente enthält (dh nicht leer ist), wird das Maximum des MyCounter-Felds verwendet, andernfalls wird 0 zurückgegeben.
quelle
Seit .Net 3.5 können Sie DefaultIfEmpty () verwenden und den Standardwert als Argument übergeben. So etwas wie eine der folgenden Möglichkeiten:
Die erste ist zulässig, wenn Sie eine NOT NULL-Spalte abfragen, und die zweite ist die Art und Weise, wie eine NULLABLE-Spalte abgefragt wird. Wenn Sie DefaultIfEmpty () ohne Argumente verwenden, ist der Standardwert der für den Typ Ihrer Ausgabe definierte, wie Sie in der Standardwertetabelle sehen können .
Das resultierende SELECT wird nicht so elegant sein, aber es ist akzeptabel.
Ich hoffe es hilft.
quelle
Ich denke, das Problem ist, was Sie tun möchten, wenn die Abfrage keine Ergebnisse hat. Wenn dies ein Ausnahmefall ist, würde ich die Abfrage in einen try / catch-Block einschließen und die Ausnahme behandeln, die die Standardabfrage generiert. Wenn es in Ordnung ist, dass die Abfrage keine Ergebnisse zurückgibt, müssen Sie herausfinden, wie das Ergebnis in diesem Fall aussehen soll. Es kann sein, dass @ Davids Antwort (oder etwas Ähnliches wird funktionieren). Das heißt, wenn der MAX immer positiv ist, kann es ausreichen, einen bekannten "schlechten" Wert in die Liste einzufügen, der nur ausgewählt wird, wenn keine Ergebnisse vorliegen. Im Allgemeinen würde ich erwarten, dass eine Abfrage, die ein Maximum abruft, einige Daten enthält, an denen gearbeitet werden kann, und ich würde den Try / Catch-Weg gehen, da Sie sonst immer gezwungen sind, zu überprüfen, ob der von Ihnen erhaltene Wert korrekt ist oder nicht. ICH'
quelle
Eine andere Möglichkeit wäre die Gruppierung, ähnlich wie Sie es in Raw SQL tun könnten:
Das einzige, was (erneutes Testen in LINQPad) zum Umschalten auf die VB LINQ-Variante führt, führt zu Syntaxfehlern in der Gruppierungsklausel. Ich bin mir sicher, dass das konzeptionelle Äquivalent leicht zu finden ist. Ich weiß nur nicht, wie ich es in VB wiedergeben soll.
Das generierte SQL wäre ungefähr so:
Das verschachtelte SELECT sieht schwierig aus, als würde die Abfrageausführung alle Zeilen abrufen und dann die passende aus dem abgerufenen Satz auswählen. Die Frage ist, ob SQL Server die Abfrage in etwas optimiert, das mit der Anwendung der where-Klausel auf das innere SELECT vergleichbar ist. Ich untersuche das jetzt ...
Ich bin nicht gut mit der Interpretation von Ausführungsplänen in SQL Server vertraut, aber es sieht so aus, als ob die WHERE-Klausel im äußeren SELECT die Anzahl der tatsächlichen Zeilen, die zu diesem Schritt führen, alle Zeilen in der Tabelle und nur die übereinstimmenden Zeilen sind wenn sich die WHERE-Klausel auf der inneren SELECT befindet. Es sieht jedoch so aus, als würden nur 1% der Kosten in den folgenden Schritt verschoben, wenn alle Zeilen berücksichtigt werden, und so oder so kommt immer nur eine Zeile vom SQL Server zurück. Vielleicht ist es also kein so großer Unterschied im großen Schema der Dinge .
quelle
Ein bisschen spät, aber ich hatte die gleiche Sorge ...
Wenn Sie Ihren Code aus dem ursprünglichen Beitrag umformulieren, möchten Sie, dass das Maximum der Menge S durch definiert wird
Berücksichtigen Sie Ihren letzten Kommentar
Ich kann Ihr Problem wie folgt umformulieren: Sie möchten das Maximum von {0 + S}. Und es sieht so aus, als ob die vorgeschlagene Lösung mit concat semantisch die richtige ist :-)
quelle
Warum nicht etwas direkteres wie:
quelle
Ein interessanter Unterschied, der erwähnenswert zu sein scheint, ist, dass FirstOrDefault und Take (1) dieselbe SQL generieren (jedenfalls laut LINQPad), FirstOrDefault jedoch einen Wert zurückgibt - den Standardwert - wenn keine übereinstimmenden Zeilen vorhanden sind und Take (1) zurückgibt Keine Ergebnisse ... zumindest in LINQPad.
quelle
Nur um alle da draußen wissen zu lassen, dass Linq to Entities verwendet wird, funktionieren die oben genannten Methoden nicht ...
Wenn Sie versuchen, so etwas zu tun
Es wird eine Ausnahme auslösen:
Ich würde vorschlagen, nur zu tun
Und das
FirstOrDefault
gibt 0 zurück, wenn Ihre Liste leer ist.quelle
quelle
Ich habe eine
MaxOrDefault
Erweiterungsmethode entwickelt. Es gibt nicht viel zu tun, aber seine Anwesenheit in Intellisense ist eine nützliche Erinnerung daran, dassMax
bei einer leeren Sequenz eine Ausnahme verursacht wird. Darüber hinaus ermöglicht die Methode die Angabe der Standardeinstellung, falls erforderlich.quelle
Für Entity Framework und LINQ to SQL können wir dies durch die Definition eine Erweiterungsmethode erreichen , die eine modifiziert
Expression
bestandenIQueryable<T>.Max(...)
Methode:Verwendung:
Die generierte Abfrage ist identisch. Sie funktioniert wie ein normaler Aufruf der
IQueryable<T>.Max(...)
Methode. Wenn jedoch keine Datensätze vorhanden sind, wird ein Standardwert vom Typ T zurückgegeben, anstatt eine Ausnahme auszulösenquelle
Ich hatte gerade ein ähnliches Problem. Meine Komponententests wurden mit Max () bestanden, schlugen jedoch fehl, wenn sie mit einer Live-Datenbank ausgeführt wurden.
Meine Lösung bestand darin, die Abfrage von der ausgeführten Logik zu trennen und sie nicht zu einer Abfrage zusammenzufügen.
Ich brauchte eine Lösung, um in Unit-Tests mit Linq-Objekten (in Linq-Objekten arbeitet Max () mit Nullen) und Linq-SQL zu arbeiten, wenn ich in einer Live-Umgebung ausgeführt werde.
(Ich verspotte die Select () in meinen Tests)
Weniger effizient? Wahrscheinlich.
Interessiert es mich, solange meine App beim nächsten Mal nicht umfällt? Nee.
quelle