【问题标题】:Linq - calculate multiple averages in one queryLinq - 在一个查询中计算多个平均值
【发布时间】:2009-06-29 11:40:54
【问题描述】:

我正在尝试将一些 SQL 查询转换为 Linq,以避免多次访问数据库。

我尝试转换的旧 SQL 是:

SELECT
    AVG(CAST(DATEDIFF(ms, A.CreatedDate, B.CompletedDate) AS decimal(15,4))),
    AVG(CAST(DATEDIFF(ms, B.CreatedDate, B.CompletedDate) AS decimal(15,4)))
FROM
    dbo.A
INNER JOIN
    dbo.B ON B.ParentId = A.Id

所以我创建了两个 C# 类:

class B
{
    public Guid Id { get; set; }
    public DateTime CreatedDate { get; set; }
    public DateTime CompletedDate { get; set; }
}

class A
{
    public Guid Id { get; set; }
    public DateTime CreatedDate { get; set; }
    public List<B> BList { get; set; }
}

我有一个要查询的List&lt;A&gt; 对象。它是从数据库中填充的,因此列表中的每个 A 都有一个带有 B 负载的子列表。我想使用 Linq-to-objects 来查询这个列表。

所以我需要使用 Linq 来获取 A 开始和完成其子 B 之间的平均时间,以及每个 B 开始和完成之间的平均时间。我没有编写原始 SQL,所以我不完全确定它是否能完成它应该做的事情!

我有几个这样的平均值要计算,所以我想在一个神奇的 Linq 查询中完成它们。这可能吗?

【问题讨论】:

    标签: c# sql linq average aggregates


    【解决方案1】:

    更有趣的问题是如何在服务器上做这样的事情,例如将以下查询转换为 LINQ to SQL。

            var q = from single in Enumerable.Range(1, 1)
                    let xs = sourceSequence
                    select new
                    {
                        Aggregate1 = xs.Sum(),
                        Aggregate2 = xs.Average(),
                        // etc
                    };
    

    但是,如果您使用的是 LINQ to Objects,那么尝试将其塞进一个查询中是没有意义的。只需单独编写聚合:

    var aggregate1 = xs.Sum();
    var aggregate2 = xs.Average();
    // etc
    

    换句话说:

    var xs = from a in listOfAs
             from b in a.Bs
             select new { A=a, B=b };
    
    var average1 = xs.Average(x => (x.B.Completed - x.A.Created).TotalSeconds);
    var average2 = xs.Average(x => (x.B.Completed - x.B.Created).TotalSeconds);
    

    我没听错吧?

    编辑:David B 的回答类似。我忘记将 TimeSpan 转换为 Average 方法支持的简单数字类型。使用 TotalMilliseconds/Seconds/Minutes 或任何适当的比例。

    【讨论】:

    • 谢谢,这可以完美地完成工作!我已经选择了单独的聚合选项 - 这让事情变得更清晰了。
    【解决方案2】:

    Sql 和 Linq 在某些方面有所不同。 Sql 具有隐式分组 - 将所有这些分组并组成一个组。在 linq 中,可以通过引入一个常量来进行分组来伪造隐式分组。

    var query = 
      from i in Enumerable.Repeat(0, 1)
      from a in AList
      from b in a.BList
      select new {i, a, b} into x
      group x by x.i into g
      select new
      {
        Avg1 = g.Average(x => (x.b.CompletedDate - x.a.CreatedDate)
           .TotalMilliseconds ),
        Avg2 = g.Average(x => (x.b.CompletedDate - x.b.CreatedDate)
           .TotalMilliseconds )
      };
    

    【讨论】:

    • 这也很棒,我最终可能会使用这个实现。要是我能有两个答案就好了!
    【解决方案3】:

    让我们从获取 B 开始和结束之间的平均时间开始:

    1) 你首先需要每个 B 对象的开始和结束之间的时间差,所以你可以这样做:

    List<TimeSpan> timeSpans = new List<TimeSpan>();
    foreach (B myB in BList)
    {
      TimeSpan myTimeSpan = myB.CompletedDate.Subtract(myB.CreatedDate);
      timeSpans.Add(myTimeSpan);
    }
    

    2) 你现在需要得到这个的平均值。您可以使用 lambda 表达式来做到这一点:

    //This will give you the average time it took to complete a task in minutes
    Double averageTimeOfB = timeSpans.average(timeSpan => timeSpan.TotalMinutes);
    

    您现在可以执行相同的操作来获取 A 开始和 B 结束之间的平均时间,如下所示:

    1)获取A的开始日期和B的每个完成日期的时间差:

     List<TimeSpan> timeSpans_2 = new List<TimeSpan>();
     foreach (B myB in BList)
     {
        TimeSpan myTimeSpan = myB.CompletedDate.Subtract(objectA.CreatedDate);
        timeSpans_2.Add(myTimeSpan);
     }
    

    2) 得到这些时间差的平均值,与上面的完全一样:

    //This will give you the average time it took to complete a task in minutes
    Double averageTimeOfAandB = timeSpans_2.average(timeSpan => timeSpan.TotalMinutes);
    

    你就完成了。我希望这个对你有用。请注意,此代码未经测试。

    编辑:请记住,LINQ 使用 Lamda 表达式,但更多的是语法糖(对实现不太了解,所以我希望你不要反对我)。您还可以将我提供的实现包装在方法中,以便您可以多次调用它们以获得 A 对象列表。

    【讨论】:

    • 谢谢,这确实可以完成这项工作。但是,由于我有 9 种平均/最小/最大等类型的东西要从这些数据中计算出来,我想知道它是否可以在一个 Linq 查询中完成,类​​似于原始 SQL。
    • 糟糕,刚刚看到您的编辑。看起来这将是最简单的方法。我在 Linq 中看到了 Average 方法,并认为它们可能是前进的方向。但也许不是。
    • 抱歉有点糊涂了,你的意思是给定一个 A 类型的对象,你不仅要计算平均值,还要计算 min/max..etc?
    • 嗯,原始 SQL 包含对数据的 9 种不同操作,计算各种平均值、最小值、最大值等。我只是为我的示例选择了两个最难的,并认为我可以从那里解决剩下的问题.很抱歉造成混乱!
    • 嗯...你可以用 .avarege 代替 .min 或 .max 并从那里开始工作?
    猜你喜欢
    • 1970-01-01
    • 2017-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多