[C#] 최대 또는 기본?

행을 반환하지 않을 수있는 LINQ 쿼리에서 Max 값을 얻는 가장 좋은 방법은 무엇입니까? 내가 방금하면

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select y.MyCounter).Max

쿼리가 행을 반환하지 않으면 오류가 발생합니다. 난 할 수 있습니다

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select y.MyCounter _
         Order By MyCounter Descending).FirstOrDefault

그러나 그것은 간단한 요청에 약간 둔감을 느낍니다. 더 좋은 방법이 누락 되었습니까?

업데이트 : 뒷이야기는 다음과 같습니다. 자식 테이블에서 다음 자격 카운터를 검색하려고합니다 (레거시 시스템, 시작하지 마십시오 …). 각 환자의 첫 번째 자격 행은 항상 1이고 두 번째는 2 등입니다 (분명히 자식 테이블의 기본 키는 아님). 따라서 환자의 최대 기존 카운터 값을 선택한 다음 1을 추가하여 새 행을 만듭니다. 기존 자식 값이 없으면 0을 반환하는 쿼리가 필요합니다. 따라서 1을 추가하면 카운터 값이 1이됩니다. 레거시 앱이 카운터 값에 차이를 일으킬 수있는 경우 자식 행의 원시 수에 의존하고 싶지 않습니다. 질문을 너무 일반적으로 만들려고 노력했기 때문에 나쁘다.



답변

이후 DefaultIfEmptySQL에 LINQ에서 구현되지 않습니다, 나는 그것이 반환 된 오류에서 검색을했고, 발견 된 매혹적인 기사 집계 함수에 널 (null) 세트를 다루고 그. 내가 찾은 것을 요약하기 위해 선택 내에서 nullable로 캐스팅 하여이 제한을 해결할 수 있습니다. 내 VB는 조금 녹슬었지만 다음과 같이 될 것이라고 생각 합니다.

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select CType(y.MyCounter, Integer?)).Max

또는 C #에서 :

var x = (from y in context.MyTable
         where y.MyField == value
         select (int?)y.MyCounter).Max();


답변

방금 비슷한 문제가 있었지만 쿼리 구문이 아닌 목록에서 LINQ 확장 메서드를 사용하고있었습니다. Nullable 트릭으로 캐스팅하면 다음과 같이 작동합니다.

int max = list.Max(i => (int?)i.MyCounter) ?? 0;


답변

DefaultIfEmpty다음과 같은 경우처럼 들립니다 (예상치 않은 코드는 다음과 같습니다).

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select y.MyCounter).DefaultIfEmpty.Max


답변

당신이 요구하는 것을 생각하십시오!

{1, 2, 3, -1, -2, -3}의 최대 값은 분명히 3입니다. {2}의 최대 값은 분명히 2입니다. 그러나 빈 세트의 최대 값 {}은 무엇입니까? 분명히 그것은 무의미한 질문입니다. 빈 세트의 최대 값은 단순히 정의되지 않았습니다. 답을 얻으려고하는 것은 수학적 오류입니다. 세트의 최대 값 자체는 해당 세트의 요소 여야합니다. 빈 집합에는 요소가 없으므로 특정 집합이 해당 집합에 속하지 않고 해당 집합의 최대 값이라고 주장하면 수학적 모순입니다.

프로그래머가 0으로 나누기를 요청할 때 컴퓨터가 예외를 throw하는 것이 올바른 동작 인 것처럼, 프로그래머가 빈 세트의 최대 값을 가져 오도록 요청할 때 컴퓨터가 예외를 throw하는 것이 올바른 동작입니다. 빈 세트의 최대 값을 취하고, spacklerorke를 자극하고, 비행 유니콘을 Neverland로 타는 것은 의미가 없으며 불가능하며 정의되지 않습니다.

이제 실제로 하고 싶은 것은 무엇입니까?


답변

항상 Double.MinValue시퀀스에 추가 할 수 있습니다 . 이렇게하면 최소한 하나의 요소 Max가 있으며 실제로 최소 인 경우에만 반환합니다. 더 효율적입니다 (옵션을 결정하기 위해 Concat, FirstOrDefault또는 Take(1)), 당신은 적절한 벤치마킹을 수행해야합니다.

double x = context.MyTable
    .Where(y => y.MyField == value)
    .Select(y => y.MyCounter)
    .Concat(new double[]{Double.MinValue})
    .Max();


답변

int max = list.Any() ? list.Max(i => i.MyCounter) : 0;

목록에 요소가없는 경우 (예 : 비어 있지 않은 경우) MyCounter 필드의 최대 값을 사용하고 그렇지 않으면 0을 반환합니다.


답변

.Net 3.5부터 DefaultIfEmpty ()를 사용하여 기본값을 인수로 전달할 수 있습니다. 다음 방법 중 하나와 같은 것 :

int max = (from e in context.Table where e.Year == year select e.RecordNumber).DefaultIfEmpty(0).Max();
DateTime maxDate = (from e in context.Table where e.Year == year select e.StartDate ?? DateTime.MinValue).DefaultIfEmpty(DateTime.MinValue).Max();

첫 번째 열은 NOT NULL 열을 쿼리 할 때 허용되며 두 번째 열은 NULLABLE 열을 쿼리하는 데 사용되는 방식입니다. 인수없이 DefaultIfEmpty ()를 사용하는 경우 기본값 표 에서 볼 수 있듯이 기본값 은 출력 유형에 정의 된 입니다.

결과 SELECT는 그렇게 우아하지는 않지만 수용 가능합니다.

도움이 되길 바랍니다.