Linq - 在一个查询中计算多个平均值

发布于 2024-07-26 05:51:36 字数 932 浏览 4 评论 0原文

我正在尝试将一些 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 对象。 它是从数据库填充的,因此列表中的每个 A 都有一个包含 B 负载的子列表。 我想使用 Linq-to-objects 来查询此列表。

因此,我需要使用 Linq 来获取 A 的开始和其子 B 的完成之间的平均时间,以及每个 B 的开始和完成之间的平均时间。 我没有编写原始 SQL,所以我不完全确定它是否达到了预期的效果!

我有几个平均值需要计算,因此我想在一个神奇的 Linq 查询中完成所有这些操作。 这可能吗?

I'm trying to convert some SQL queries into Linq to avoid multiple trips to the database.

The old SQL I'm trying to convert does:

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

So I've created two C# classes:

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; }
}

And I've got a List<A> object that I want to query. It's populated from the database, so each A in the list has a sub-list with a load of Bs. I want to use Linq-to-objects to query this list.

So I need to use Linq to get the average time between an A's start and the completion of its child Bs, and the average time between each B's start and completion. I didn't write the original SQL so I'm not entirely sure it does what it's supposed to!

I've got several of these averages to calculate, so I'd like to do them all inside one magical Linq query. Is this possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

来世叙缘 2024-08-02 05:51:36

更有趣的问题是如何在服务器上执行此类操作,例如将以下查询转换为 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 方法支持的简单数字类型。 使用总毫秒/秒/分钟或任何合适的比例。

The more interesting question would be how to do such a thing on the server, for example to make the following query translate to LINQ to SQL.

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

But there's no point trying to cram it into one query if you're using LINQ to Objects. Just write the aggregates separately:

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

In other words:

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);

Did I understand right?

Edit: David B answered similarly. I forgot to convert the TimeSpan to a simple numeric type supported by the Average method. Use TotalMilliseconds/Seconds/Minutes or whatever scale is appropriate.

城歌 2024-08-02 05:51:36

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 )
  };

Sql and Linq differ on certain points. Sql has implicit grouping - take all of these and make one group. In linq, one may fake implicit grouping by introducing a constant to group on.

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 )
  };
栩栩如生 2024-08-02 05:51:36

让我们从获取 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 对象列表多次调用它们。

Lets start by getting the average time between B's start and finish:

1) You first need the time difference between the start and finish of each B object so you would do this:

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

2) You now need to get the average of this. You can do this by using lambda expressions:

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

You can now do the same thing to get the average time between A's start and B's finish as follows:

1)Get the time difference for A's start date and each of B's completion date:

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

2) Get the average of these time differnces exactly like the one above:

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

And you're done. I hope that this helps. Please note that this code has not been tested.

EDIT: Remember that LINQ uses Lamda Expressions but is more of syntactic sugar (don't know much about the implementation so I hope you don't hold it against me). And also you could wrap the implementaions I provide in methods so that you can call them multiple times for a List of A objects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文