LINQ to SQL:对来自订购系统的多个表的报告的聚合数据进行复杂查询

Posted

技术标签:

【中文标题】LINQ to SQL:对来自订购系统的多个表的报告的聚合数据进行复杂查询【英文标题】:LINQ to SQL: Complicated query with aggregate data for a report from multiple tables for an ordering system 【发布时间】:2010-10-05 12:10:26 【问题描述】:

我想将以下查询转换为 LINQ 语法。我在设法让它工作时遇到了很多麻烦。我实际上尝试从 LINQ 开始,但发现如果我反过来写它可能会更好。

SELECT
    pmt.guid,
    pmt.sku,
    pmt.name,
    opt.color,
    opt.size,
    SUM(opt.qty) AS qtySold,
    SUM(opt.qty * opt.itemprice) AS totalSales,
    COUNT(omt.guid) AS betweenOrders
FROM
    products_mainTable pmt 
        LEFT OUTER JOIN 
            orders_productsTable opt ON opt.products_mainTableGUID = pmt.guid
            LEFT OUTER JOIN orders_mainTable omt ON omt.guid = opt.orders_mainTableGUID AND
                (omt.flags & 1) = 1
GROUP BY
    pmt.sku, opt.color, opt.size, pmt.guid, pmt.name
ORDER BY
    pmt.sku

最终结果是一个表格,上面显示了有关产品的信息。 如何使用理解语法以 LINQ 形式编写此查询?

此外,我可能想要添加额外的过滤器(例如,添加到 orders_mainTable)。

这是我尝试制作的一个示例,它相当接近,但不确定它是否是“正确”的方式,并且无法从 orders_productsTable 中按大小和颜色对其进行分组。

from pmt in products_mainTable
let Purchases = 
        from opt in pmt.orders_productsTable
        where ((opt.orders_mainTable.flags & 1) == 1)
        where ((opt.orders_mainTable.date_completedon > Convert.ToDateTime("01/01/2009 00:00:00")))
        select opt
orderby pmt.sku
select new 
    pmt.guid,
    pmt.sku,
    pmt.name,
    pmt.price,  
    AvgPerOrder = Purchases.Average(p => p.qty).GetValueOrDefault(0),
    QtySold = Purchases.Sum(p => p.qty).GetValueOrDefault(),
    SoldFor = Purchases.Sum(p => p.itemprice * p.qty).GetValueOrDefault()           

*编辑:

为了更明确一点,以便您了解我想要做什么,这里有更多解释。

产品存储在 products_mainTable 订单存储在 orders_mainTable 已订购的产品存储在 orders_productsTable 中

我想根据产品、订单等创建多个报告。钻取数据并找到有意义的位以显示给最终用户。

在本例中,我试图显示在一段时间内购买了哪些产品并且最受欢迎。卖出了多少,价格是多少,每个订单的突破是多少。也许不是最好的顺序,但我只是在试验并选择了这个。

所有表都与其他表有关系。所以从产品表中,我可以得到订购该产品的订单等等。

我遇到的最大问题是了解 LINQ 的工作原理,尤其是在分组、聚合数据、扩展、子查询等方面。这很有趣,但开始变得令人沮丧,因为我很难找到有关如何这样做。

【问题讨论】:

这种事情是 LINQ 变得复杂的地方,并开始引入性能问题。您可以尝试使用您发布的 SQL 编写视图,然后将 LINQ 指向该视图。 我没有想到的事情,谢谢深渊骑士。 【参考方案1】:

我也是 LINQ 的初学者。我不知道这是否是按多个字段分组的正确方法,但我认为您必须将这些分组字段转换为表示键。因此,假设您的所有分组字段都是字符串或整数,您可以按如下方式创建一个键:

        var qry = from pmt in products_mainTable
                  join opt in orders_productsTable on pmt.guid equals opt.products_mainTableGUID
                  join omt in orders_mainTable on opt.orders_mainTableGUID equals omt.guid
                  where (opt.orders_mainTable.flags & 1) == 1
                  group omt by pmt.sku + opt.price + opt.size + pmt.guid + pmt.name into g
                  orderby g.sku
                  select new
                  
                      g.FirstOrDefault().guid,
                      g.FirstOrDefault().sku,
                      g.FirstOrDefault().name,
                      g.FirstOrDefault().color,
                      g.FirstOrDefault().price,
                      AvgPerOrder = g.Average(p => p.qty).GetValueOrDefault(0),
                      QtySold = g.Sum(p => p.qty).GetValueOrDefault(),
                      SoldFor = g.Sum(p => p.itemprice * p.qty).GetValueOrDefault()
                  ;

我没有对此进行测试,所以请看看这是否对您有任何帮助。

【讨论】:

布鲁诺,帮了大忙!谢谢。【参考方案2】:

布鲁诺,非常感谢您的帮助! FirstOrDefault() 可能是最大的帮助。遵循您所做的一些事情,以及另一个资源,我想出了以下似乎效果很好的资源!下面的这个 LINQ 查询几乎完全复制了我上面发布的 SQL。

这是我在 LINQ 中执行 LEFT OUTER JOIN 时发现的另一个资源:Blog Post

最终答案:

 from pmt in products_mainTable
    join opt in orders_productsTable on pmt.guid equals opt.products_mainTableGUID into tempProducts
    from orderedProducts in tempProducts.DefaultIfEmpty()
    join omt in orders_mainTable on orderedProducts.orders_mainTableGUID equals omt.guid into tempOrders
    from ordersMain in tempOrders.DefaultIfEmpty()
    group pmt by new  pmt.sku, orderedProducts.color, orderedProducts.size  into g
    orderby g.FirstOrDefault().sku
    select new 
        g.FirstOrDefault().guid,
        g.Key.sku,
        g.Key.size, 
        QTY = g.FirstOrDefault().orders_productsTable.Sum(c => c.qty),
        SUM = g.FirstOrDefault().orders_productsTable.Sum(c => c.itemprice * c.qty),
        AVG = g.FirstOrDefault().orders_productsTable.Average(c => c.itemprice * c.qty),
        Some = g.FirstOrDefault().orders_productsTable.Average(p => p.qty).GetValueOrDefault(0),
    

【讨论】:

【参考方案3】:

这对我很有帮助,谢谢。我有一个类似的问题,我试图解决只是我的案例要简单得多,因为我没有任何连接。我只是想对一个字段进行分组,得到另一个字段的最小值和计数。 (同一查询中的最小值和计数)

这是我想用 Linq 语法重新创建的 SQL:

select t.Field1, min(t.Field2), COUNT(*)
from SomeTable t
group by t.Field1
order by t.Field1

感谢你的帖子,我终于想出了这个:

from t in SomeTable
group t by new  t.Field1  into g
orderby g.Key.Field1
select new
 
  g.Key.Field1,
  code = g.Min(c => c.Field2),
  qty = g.Count()

这会在后台创建以下 SQL:

SELECT [t1].[Field1], [t1].[value] AS [code], [t1].[value2] AS [qty]
FROM (
    SELECT MIN([t0].[Field2]) AS [value], COUNT(*) AS [value2], [t0].[Field1]
    FROM [SomeTable] AS [t0]
    GROUP BY [t0].[Field1]
    ) AS [t1]
ORDER BY [t1].[Field1]

完美,正是我想要做的。对我而言,关键是您展示了在新的 中执行此操作的可能性,这是我从未考虑过尝试的事情。这是巨大的,我现在觉得我对未来有了更好的理解。

【讨论】:

很棒的约翰!很高兴我的问题/答案对您有所帮助。谢谢你的分享。我还必须说,我喜欢 Stack Overflow。 :P

以上是关于LINQ to SQL:对来自订购系统的多个表的报告的聚合数据进行复杂查询的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to SQL语句之Join

Linq to Sql - 具有父表关系的双重分组

Linq-to-sql 不产生多个外连接?

Linq to SQL 的连表查询(转)

在 ASP.NET MVC 中向控制器传递多个参数;此外,在 LINQ-to-SQL 中生成动态查询

Linq-To-Sql ORM 更新一对多