5

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

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

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

4

3 回答 3

5

更有趣的问题是如何在服务器上做这样的事情,例如将以下查询转换为 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);

我理解对了吗?

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.

于 2009-06-29T13:56:18.593 回答
4

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 )
  };
于 2009-06-29T13:53:34.650 回答
1

让我们从获取 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 对象列表。

于 2009-06-29T12:38:39.733 回答