【问题标题】:Sum of multiple queries to optimise number of queries performed多个查询的总和以优化执行的查询数量
【发布时间】:2013-06-28 11:00:39
【问题描述】:

我有一个查询,它必须对几个表中的值求和并添加结果。该系统只是一个库存系统,我试图通过计算进货 (deliveries)、支出 (issues) 和adjustments 来获取库存水平。

由于库存水平是一个计算值(sum(deliveries) - sum(issues)) + sum(adjustments),我正在尝试创建一个函数,该函数将以最少的查询次数获得该值。

目前我有 linq,它执行三个单独的查询来获取每个求和值,然后在我的函数中执行加法/减法,但是我相信必须有更好的方法来计算值,而不必执行三个单独的查询.

目前的功能如下:

public static int GetStockLevel(int itemId)
{
    using (var db = EntityModel.Create())
    {
        var issueItemStock = db.IssueItems.Where(x => x.ItemId == itemId).Sum(x => x.QuantityFulfilled);
        var deliveryItemStock = db.DeliveryItems.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
        var adjustmentsStock = db.Adjustments.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
        return (deliveryItemStock - issueItemStock) + adjustmentsStock;
    }
}

在我看来,SQL 查询很简单,所以我考虑了一个存储过程,但是我认为必须有一种方法可以使用 linq 来做到这一点。

非常感谢

编辑:回答

从 Ocelot20 的答案中获取代码,稍作改动。每个lets 都可以返回一个空值,如果是,那么 linq 会抛出一个异常。使用DefaultIfEmpty 命令将否定这一点,并返回 0 进行最终计算。我使用的实际代码如下:

from ii in db.Items
let issueItems = db.IssueItems.Where(x => x.ItemId == itemId).Select(t => t.QuantityFulfilled).DefaultIfEmpty(0).Sum()
let deliveryItemStock = db.DeliveryItems.Where(x => x.ItemId == itemId).Select(t => t.Quantity).DefaultIfEmpty(0).Sum()
let adjustmentsStock = db.Adjustments.Where(x => x.ItemId == itemId).Select(t => t.Quantity).DefaultIfEmpty(0).Sum()
select (deliveryItemStock - issueItems) + adjustmentsStock);

【问题讨论】:

  • 您考虑过使用联接吗?见stackoverflow.com/a/9722744/1341477
  • SQL: select (select count(*) from a) - (select count(*) from b) + (select count(*) from c)
  • 我查看了连接,但我看不出他们将如何应用这种情况

标签: sql-server linq entity-framework optimization


【解决方案1】:

在不知道您的实体长什么样的情况下,您可以执行以下操作:

public static int GetStockLevel(int itemId)
{
    using (var db = EntityModel.Create())
    {
        // Note: Won't work if there are no IssueItems found.
        return (from ii in db.IssueItems
                let issueItems = db.IssueItems.Where(x => x.ItemId == itemId)
                                              .Sum(x => x.QuantityFulfilled)
                let deliveryItemStock = db.DeliveryItems.Where(x => x.ItemId == itemId)
                                                        .Sum(x => x.Quantity)
                let adjustmentsStock = db.Adjustments.Where(x => x.ItemId == itemId)
                                                     .Sum(x => x.Quantity)
                select issueItems + deliveryItemStock + adjustmentsStock).FirstOrDefault() ?? 0;
    }
}

我在自己的数据库上测试了一个类似的查询,它在单个查询中运行。我怀疑由于它们都有一个共同的ItemId,因此使用实体关系可能会使它看起来像:

// Ideal solution:
(from i in db.Items
 where i.Id == itemId
 let issueItems = i.IssueItems.Sum(x => x.QuantityFulfilled)
 let deliveryItemStock = i.DeliveryItems.Sum(x => x.Quantity)
 let adjustmentsStock = i.Adjustments.Sum(x => x.Quantity)
 select issueItems + deliveryItemStock + adjustmentsStock).SingleOrDefault() ?? 0;

【讨论】:

  • 这是最接近让它工作的方法,但是当表中没有匹配的记录时,返回值为 null,而不是 0。即,如果 Item 13 的 IssueItems 有 5 条记录,但DeliveryItems 或 Adjustments 中没有任何内容,则返回结果为 null 而不是 5。
  • @Ryan Amies:在第二种情况下,.FirstOrDefault() ?? 0.SingleOrDefault() ?? 0 很容易解决。编辑 - 抱歉,第一个解决方案不起作用,但如果第二个解决方案存在(不知道为什么不存在),您应该能够使用 SingleOrDefault 解决该问题。
  • @RyanAmies:请参阅更新以回答。
  • 我已将此标记为答案,但问题的实际解决方案已在我更新的问题中看到。
  • @RyanAmies:您尝试过我发布的第二种方法吗?如果您没有任何 IssueItems,您发布的答案仍然会出现问题,因为这是查询的基础。
【解决方案2】:

您是否考虑过向数据库添加一个视图来执行计算,然后您可以使用简单的选择查询(或 SP)来返回您需要的值?

【讨论】:

  • 是的,但如果可能的话,我想将所有数据访问保留在我的服务层中
【解决方案3】:

我认为这应该可行,并且生成的 SQL 并不是特别复杂。如果您认为它有问题,请告诉我,我会更新我的答案。

public static int GetStockLevel(int itemId)
{
    using (var db = EntityModel.Create())
    {
        return db.IssueItems.Where(x => x.ItemId == itemId).GroupBy(x => x.ItemId)
        .GroupJoin(db.DeliveryItems, x => x.First().ItemId, y => y.ItemId, (x, y) => new 
        { Issues = x, Deliveries = y})
        .GroupJoin(db.Adjustments, x=> x.Issues.First().ItemId, y=> y.ItemId, (x, y) => new 
        {
            IssuesSum = x.Issues.Sum(i => i.QuantityFullfilled), 
            DeliveriesSum = x.Deliveries.Sum(d => d.Quantity), 
            AdjustmentsSum = y.Sum(a => a.Quantity)})
        .Select(x => x.IssuesSum - x.DeliverysSum + x.AdjustmentsSum);
    }
}

【讨论】:

  • 第二个GroupJoin 出错,因为第一个GroupJoin 返回的对象是匿名类型
  • 抱歉,您忘记了要退回收藏品。我已更新以包含 First()
  • 遭受同样的问题@RyanAmies 在我第一次尝试时指出。如果没有 IssueItems,则忽略其他计数。
  • 在这种情况下你可以只使用 DefaultIfEmpty() 吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 2014-12-28
相关资源
最近更新 更多