【问题标题】:How to select aggregate of multiple columns (no group by)?如何选择多列的聚合(无分组依据)?
【发布时间】:2012-02-13 03:50:47
【问题描述】:

假设我有一个这样的汇总表:

AcitivityCache
(
   pkId (bigint, PK),
   Date (DateTime),
   OfficeId (int, FK)
   TransactionCount (int),
   RejectCount (int),
   InvitationCount (int),
   RequestCount (int)
   ...
   --more counts here--
   ...
)

现在我需要获取一段时间内的总计数(不按办公室分组,只是总和),我可以像这样使用 SQL 来做:

 SELECT SUM(TransactionCount) as TotalTxCount, SUM(RejectCount) as TotalRejectCount, ...
 FROM ActivityCache 
 WHERE [Date] between '2011-02-01' and '2012-02-01'

如何使用 linq 实现这一点? 到目前为止,我看到的大多数解决方案都有一些分组。但是对于这种情况,我不需要分组,只需要总数。我可以这样做:

DateTime dateFrom, toDate; // initialized later
...
var q = dbContext.ActivityCaches
                 .Where(a=> a.Date > fromDate && a.Date <= toDate)
                 .GroupBy(a=> 1)
                 .Select(g=> new 
                             {
                                TotalTransactionCount = g.Sum(a => a.TransactionCount),
                                TotalRejectCount = g.Sum(a => a.RejectCount),
                                ...
                                ...
                             });

或者,我可以先获取列,然后以编程方式求和。 或者,我可以使用 SP(在这种情况下不喜欢)。 任何更好的想法(没有分组)?

P.S:我无法更改数据库架构

【问题讨论】:

  • 嗯,T-SQL 是隐式进行分组操作,只是所有记录都在同一个组中,这与您对 @ 所做的操作相同987654324@。你有什么特别的理由不喜欢这样吗?
  • 没有理由不喜欢 :)。我只是想知道是否还有更多方法。

标签: c# .net linq entity-framework linq-to-entities


【解决方案1】:

我不认为 LINQ to Entities 对这种情况有一些很好的查询。
另一种解决方案是使用Entity SQL (ESQL)

var fromDate = new DateTime(2011, 02, 01);
var toDate = new DateTime(2012, 02, 01);
var esql = @"select 
          sum(it.TransactionCount) as SumOfTransactionCount,
          sum(it.RejectCount) as SumOfRejectCount
               from ActivityCaches as it
               where it.Date > @fromDate and it.Date <= @toDate";
var query = CreateQuery<DbDataRecord>(esql,
        new ObjectParameter("fromDate", fromDate) ,
        new ObjectParameter("toDate", toDate));

【讨论】:

  • 我不喜欢字符串查询。但是,似乎您说“我不认为 LINQ to Entities 对这种情况有一些很好的查询”是对的。我会坚持'.GroupBy(g=>1)'
【解决方案2】:

另一种方式:

  1. 使用实体 SQL
  2. 应用 3 个单独的SUMs

【讨论】:

    【解决方案3】:

    从您的问题中不清楚您要将结果存储在哪里。如果您打算将它存储在两个变量中,那么您可以获取数据,使用 .Where() 扩展方法按日期范围过滤,然后使用 .Sum() 扩展方法计算总和值。大致如下:

    var result = your_query.Where(m => m.Date >= startingDate && m.Date <= endingDate);
    var totalTxCount = result.Sum(m => m.TransactionCount);
    var totalRejectCount = result.Sum(m => m.RejectCount);
    

    【讨论】:

      【解决方案4】:

      Linq Aggregate() 方法允许求和而不分组,如下所示。恐怕我还没有针对数据库设置它,所以我不能保证不会弹出 Linq to Sql 错误。如果发生这种情况,您可以在执行 Aggregate() 之前但在 where() 之后尝试 ActivityCaches() 上的显式 ToList()。

      更新

      我更新了代码以使用 EF 模型。 创建了一个小型 EF 模型来替换 SomeEntry 并更新数据以确保我正在从数据库中读取数据。

      private static ActivityLog Aggregate(ActivityLog agg, ActivityLog entry) {
          agg.TranCount += entry.TranCount;
          agg.ReqCount += entry.ReqCount;
          return agg;
      }
      
      [TestMethod]
      public void AggregateFirstThree() {
      
          var context = new TestDBEntities();
      
          var startDate = new DateTime(2012, 1, 1);
          var endDate = new DateTime(2012, 1, 3);
      
          var aggregate = new ActivityLog();
      
          context.ActivityLogs.Where(entry => entry.Timestamp >= startDate && entry.Timestamp <= endDate).Aggregate(aggregate, Aggregate);
      
          Assert.AreEqual(66, aggregate.TranCount);
          Assert.AreEqual(36, aggregate.ReqCount);
      
      }
      
      [TestMethod]
      public void AggregateLastThree() {
      
          var context = new TestDBEntities();
      
          var startDate = new DateTime(2012, 1, 4);
          var endDate = new DateTime(2012, 1, 6);
      
          var aggregate = new ActivityLog();
      
          context.ActivityLogs.Where(entry => entry.Timestamp >= startDate && entry.Timestamp <= endDate).Aggregate(aggregate, Aggregate);
      
          Assert.AreEqual(75, aggregate.TranCount);
          Assert.AreEqual(45, aggregate.ReqCount);
      
      }
      

      hth,
      艾伦。

      【讨论】:

      • @Adrian Iftode 刚刚尝试过,EF 模型,运行良好。甚至不需要添加 ToList()。将更新代码以反映更改。
      • 好的,我也检查了这个。不幸的是,它并没有将其转换为预期的 SQL 语句。它提取所有行,然后在内存中进行聚合(因为 List 将被调用)
      • @Adrian Iftode 好的。这是否意味着它不起作用,或者它没有在服务器上将聚合作为 SQL 运行。从 OP 我不认为这是一项要求。
      • 这意味着它不会在 SQL 上运行聚合。 OP 想要 select sum(), count(), .. from table,所以要在服务器上运行聚合,而不是通过 .Net
      • “LINQ to Entities 无法识别方法 Aggregate”。但我不想调用 ToList();同意阿德里安...感谢您的努力
      猜你喜欢
      • 2020-01-17
      • 1970-01-01
      • 2020-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多