Jaki jest najlepszy sposób uzyskania wartości Max z zapytania LINQ, które może nie zwracać żadnych wierszy? Jeśli po prostu to zrobię
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter).Max
Pojawia się błąd, gdy zapytanie nie zwraca żadnych wierszy. mógłbym zrobić
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter _
Order By MyCounter Descending).FirstOrDefault
ale wydaje się to trochę tępe jak na tak prostą prośbę. Czy brakuje mi lepszego sposobu, aby to zrobić?
AKTUALIZACJA: Oto historia: próbuję pobrać następny licznik uprawnień z tabeli podrzędnej (starszy system, nie zaczynaj mnie ...). Pierwszy wiersz uprawnień dla każdego pacjenta to zawsze 1, drugi to 2 itd. (Oczywiście nie jest to klucz podstawowy tabeli podrzędnej). Tak więc wybieram maksymalną istniejącą wartość licznika dla pacjenta, a następnie dodaję do niej 1, aby utworzyć nowy wiersz. Gdy nie ma żadnych istniejących wartości podrzędnych, potrzebuję zapytania, aby zwrócić 0 (więc dodanie 1 da mi wartość licznika 1). Zauważ, że nie chcę polegać na nieprzetworzonej liczbie wierszy podrzędnych, na wypadek gdyby starsza aplikacja wprowadziła luki w wartościach liczników (możliwe). Moja wina, że próbowałem uczynić to pytanie zbyt ogólnym.
źródło
Max
plikDateTime
.Max(x => (DateTime?)x.TimeStamp)
wciąż jedyny sposób ..Właśnie miałem podobny problem, ale korzystałem z metod rozszerzenia LINQ na liście, a nie na składni zapytania. Rzutowanie na sztuczkę Nullable również działa tutaj:
źródło
Brzmi jak przypadek
DefaultIfEmpty
(nieprzetestowany kod poniżej):źródło
var colCount = RowsEnumerable.Select(row => row.Cols.Count).DefaultIfEmpty().Max()
Zastanów się, o co pytasz!
Maksimum {1, 2, 3, -1, -2, -3} to oczywiście 3. Maksimum z {2} to oczywiście 2. Ale jakie jest maksimum pustego zbioru {}? Oczywiście jest to bezsensowne pytanie. Po prostu nie zdefiniowano maksimum pustego zestawu. Próba uzyskania odpowiedzi jest błędem matematycznym. Maksimum dowolnego zestawu musi samo być elementem w tym zestawie. Pusty zbiór nie ma elementów, więc twierdzenie, że jakaś konkretna liczba jest maksimum tego zbioru bez znajdowania się w tym zbiorze, jest matematyczną sprzecznością.
Tak jak poprawnym zachowaniem jest rzucanie przez komputer wyjątku, gdy programista prosi go o podzielenie przez zero, tak też poprawnym zachowaniem jest rzucanie przez komputer wyjątku, gdy programista prosi go o maksymalne wykorzystanie pustego zestawu. Dzielenie przez zero, maksymalne wykorzystanie pustego zestawu, poruszanie spacklerorke i jazda na latającym jednorożcu do Nibylandii są bez znaczenia, niemożliwe, nieokreślone.
Co właściwie chcesz zrobić?
źródło
Zawsze możesz dodać
Double.MinValue
do sekwencji. Zapewniłoby to istnienie co najmniej jednego elementu iMax
zwróciłoby go tylko wtedy, gdy jest to faktycznie minimum. Aby określić, która opcja jest bardziej wydajna (Concat
,FirstOrDefault
lubTake(1)
), należy przeprowadzić odpowiednią analizę porównawczą.źródło
Jeśli lista zawiera jakieś elementy (tj. Nie jest pusta), zajmie maksimum pola MyCounter, w przeciwnym razie zwróci 0.
źródło
Od .Net 3.5 możesz używać DefaultIfEmpty () przekazując domyślną wartość jako argument. Coś jak jeden z następujących sposobów:
Pierwsza z nich jest dozwolona, gdy wykonujesz zapytanie o kolumnę NOT NULL, a druga to sposób, w jaki został użyty do zapytania o kolumnę NULLABLE. Jeśli użyjesz DefaultIfEmpty () bez argumentów, domyślną wartością będzie ta zdefiniowana dla typu twojego wyniku, jak widać w tabeli wartości domyślnych .
Powstały SELECT nie będzie tak elegancki, ale można go zaakceptować.
Mam nadzieję, że to pomoże.
źródło
Myślę, że chodzi o to, co chcesz, aby się stało, gdy zapytanie nie ma wyników. Jeśli jest to wyjątkowy przypadek, zawinąłbym zapytanie w blok try / catch i obsłużył wyjątek generowany przez standardowe zapytanie. Jeśli zapytanie nie zwraca żadnych wyników, musisz dowiedzieć się, jaki ma być wynik w takim przypadku. Możliwe, że odpowiedź @ David (lub coś podobnego zadziała). Oznacza to, że jeśli MAX zawsze będzie dodatni, może wystarczyć wstawienie znanej „złej” wartości do listy, która zostanie wybrana tylko wtedy, gdy nie będzie żadnych wyników. Generalnie spodziewałbym się, że zapytanie, które pobiera maksimum, ma pewne dane do pracy, i wybrałbym trasę try / catch, ponieważ w przeciwnym razie zawsze jesteś zmuszony sprawdzić, czy uzyskana wartość jest poprawna, czy nie. JA'
źródło
Inną możliwością byłoby grupowanie, podobne do tego, jak można podejść do tego w surowym SQL:
Jedyną rzeczą jest to (testowanie ponownie w LINQPad) przełączenie na smak VB LINQ powoduje błędy składniowe w klauzuli grupującej. Jestem pewien, że konceptualny odpowiednik jest dość łatwy do znalezienia, po prostu nie wiem, jak odzwierciedlić go w VB.
Wygenerowany kod SQL byłby podobny do:
Zagnieżdżony SELECT wygląda źle, jakby wykonanie zapytania pobrał wszystkie wiersze, a następnie wybrał pasujący z pobranego zestawu ... pytanie brzmi, czy SQL Server optymalizuje zapytanie do czegoś porównywalnego z zastosowaniem klauzuli where do wewnętrznego SELECT. Patrzę teraz na to ...
Nie jestem dobrze zorientowany w interpretowaniu planów wykonania w SQL Server, ale wygląda na to, że gdy klauzula WHERE znajduje się na zewnętrznym polu SELECT, liczba rzeczywistych wierszy powodujących ten krok to wszystkie wiersze w tabeli, a tylko pasujące wiersze gdy klauzula WHERE znajduje się w wewnętrznym SELECT. To powiedziawszy, wygląda na to, że tylko 1% kosztu jest przenoszony do następnego kroku, gdy uwzględniane są wszystkie wiersze, a tak czy inaczej tylko jeden wiersz wraca z serwera SQL, więc może nie jest to aż tak duża różnica w ogólnym schemacie rzeczy .
źródło
trochę późno, ale miałem ten sam problem ...
Ponownie zapisując swój kod z oryginalnego postu, chcesz, aby maksimum zestawu S było zdefiniowane przez
Biorąc pod uwagę Twój ostatni komentarz
Mogę sformułować Twój problem następująco: Chcesz maksymalnie {0 + S}. I wygląda na to, że proponowane rozwiązanie z concat jest semantycznie właściwe :-)
źródło
Dlaczego nie coś bardziej bezpośredniego, na przykład:
źródło
Ciekawą różnicą, która wydaje się warta odnotowania, jest to, że podczas gdy FirstOrDefault i Take (1) generują ten sam kod SQL (zgodnie z LINQPad, tak czy inaczej), FirstOrDefault zwraca wartość - domyślną - gdy nie ma pasujących wierszy, a Take (1) zwraca brak wyników ... przynajmniej w LINQPad.
źródło
Aby wszyscy wiedzieli, że używa Linq do Entities, powyższe metody nie będą działać ...
Jeśli spróbujesz zrobić coś takiego
Zrzuci wyjątek:
Sugerowałbym po prostu zrobić
A
FirstOrDefault
zwróci 0, jeśli lista jest pusta.źródło
źródło
Wrzuciłem
MaxOrDefault
metodę rozszerzenia. Nie ma tego wiele, ale jego obecność w Intellisense jest użytecznym przypomnieniem, żeMax
w pustej sekwencji spowoduje wyjątek. Ponadto metoda umożliwia określenie wartości domyślnej, jeśli jest to wymagane.źródło
Dla Entity Framework i Linq to SQL możemy to osiągnąć poprzez zdefiniowanie metody rozszerzenia, która modyfikuje
Expression
przekazaną doIQueryable<T>.Max(...)
metody:Stosowanie:
Wygenerowane zapytanie jest identyczne, działa jak normalne wywołanie
IQueryable<T>.Max(...)
metody, ale jeśli nie ma rekordów, zwraca domyślną wartość typu T zamiast rzucać wyjątekźródło
Właśnie miałem podobny problem, moje testy jednostkowe przeszły pomyślnie przy użyciu Max (), ale zakończyły się niepowodzeniem, gdy zostały uruchomione w działającej bazie danych.
Moim rozwiązaniem było oddzielenie zapytania od wykonywanej logiki, a nie połączenie ich w jedno zapytanie.
Potrzebowałem rozwiązania do pracy w testach jednostkowych przy użyciu obiektów Linq (w Linq-objects Max () działa z wartościami zerowymi) i Linq-sql podczas wykonywania w środowisku na żywo.
(Kpię z Select () w moich testach)
Mniej wydajne? Prawdopodobnie.
Czy mnie to obchodzi, o ile moja aplikacja nie spadnie następnym razem? Nie.
źródło