簡體   English   中英

使用linq在一個查詢中獲取特定字段的最小值和最大值

[英]Using linq to get min and max of a particular field in one query

假設您有一個類似的課程:

public class Section {
   public DateTime  StartDate;
   public DateTime? EndDate;
}

我有這些對象的列表,我想獲得最小的開始日期和最大的結束日期,但是我想使用一個linq查詢,所以我知道我只遍歷該列表一次。

例如,如果我在沒有linq的情況下執行此操作,我的代碼將看起來像這樣(不檢查null):

DateTime? minStartDate;
DateTime? maxEndDate;
foreach(var s in sections) {
     if(s.StartDate < minStartDate) minStartDate = s.StartDate;
     if(s.EndDate > maxEndDate) maxEndDate = s.EndDate;
}

我可以使用兩個linq查詢來獲取最小值和最大值,但是我知道在幕后,它需要對所有值進行兩次迭代。

之前我已經看過最小和最大查詢,但是有分組查詢。 在不進行分組的情況下,如何在單個linq查詢中如何做到這一點?

在不進行分組的情況下,如何在單個linq查詢中如何做到這一點?

如果必須這樣做,那么我會這樣做:

var minMax = (from s0 in sections
  from s1 in sections
  orderby s0.StartDate, s1.EndDate descending
  select new {s0.StartDate, s1.EndDate}).FirstOrDefault();

但我還要考慮性能問題,具體取決於所討論的提供程序。

在數據庫上,我希望它會變成這樣:

SELECT s0.StartDate, s1.EndDate
FROM Sections AS s0
CROSS JOIN Sections AS s1
ORDER BY created ASC, EndDate DESC 
LIMIT 1

要么

SELECT TOP 1 s0.StartDate, s1.EndDate
FROM Sections AS s0, Sections AS s1
ORDER BY created ASC, EndDate DESC 

取決於數據庫類型。 依次執行該方法的方式可能是兩次表掃描,但是如果我要關心這些日期,我將在這些列上有索引,因此應該在每個索引的末尾進行兩次索引外觀掃描,所以我希望它很快。

我有這些對象的清單

然后,如果我非常關心性能,就不會使用Linq。

但我想使用一個linq查詢,所以我知道我只遍歷列表一次

這就是為什么我不使用linq的原因。 由於linq中沒有用於處理此特定情況的任何內容,因此會遇到更糟糕的組合。 確實,這將比2次迭代更糟糕,這將是N +1次迭代,其中N是Sections中元素的數量。 Linq提供程序很好,但是並不是魔術。

如果我真的希望能夠在Linq中做到這一點,例如我有時對內存中的列表,有時對數據庫等等,那么我將添加自己的方法來盡可能地做到最好:

public static Tuple<DateTime, DateTime?> MinStartMaxEnd(this IQueryable<Section> source)
{
  if(source == null)
    return null;
  var minMax = (from s0 in source
  from s1 in source
  orderby s0.StartDate, s1.EndDate descending
  select new {s0.StartDate, s1.EndDate}).FirstOrDefault();
  return minMax == null ? null : Tuple.Create(minMax.StartDate, minMax.EndDate);
}
public static Tuple<DateTime, DateTime?> MinStartMaxEnd(this IEnumerable<Section> source)
{
  if(source != null)
    using(var en = source.GetEnumerator())
      if(en.MoveNext())
      {
        var cur = en.Current;
        var start = cur.StartDate;
        var end = cur.EndDate;
        while(en.MoveNext())
        {
          cur = en.Current;
          if(cur.StartDate < start)
            start = cur.StartDate;
          if(cur.EndDate.HasValue && (!end.HasValue || cur.EndDate > end))
            end = cur.EndDate;
        }
        return Tuple.Create(start, end);
      }
  return null;
}

但我想使用一個linq查詢,所以我知道我只遍歷列表一次

回到這個。 Linq不承諾對列表進行一次迭代。 它有時可以這樣做(或根本不迭代)。 它可以調用數據庫查詢,這些查詢又將概念上的幾次迭代轉化為一兩次(與CTE相同)。 它可以產生非常有效的代碼,以處理各種相似但不完全相同的查詢,其中手工編碼的替代方案可能會遭受很多浪費,或者編寫大量相似但卻不可行的查詢。不完全相同的方法。

但是,如果您假設Linq給您一次通行證,它還可以隱藏一些N + 1或N * N行為,看起來似乎少得多。 如果您需要特殊的單通行為,請添加到Linq; 它是可擴展的。

您可以使用MinMax

List<Section> test = new List<Section>();

minStartDate = test.Min(o => o.StartDate);
maxEndDate = test.Max(o => o.EndDate);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM