【问题标题】:Grouping Joined Tables对连接表进行分组
【发布时间】:2019-10-13 04:34:48
【问题描述】:

我正在尝试加入产品和属性表并按产品 ID 对它们进行分组,以获取具有其属性的产品列表。所以我尝试了以下实体框架查询:

    var productsWithAttributes = (from product in ctx.products
                                  join attribute in ctx.attributes on product.id equals attribute.productId
                                  select new
                                     {
                                          product = product,
                                          a1 = attribute.a1,
                                          a2 = attribute.a2,
                                          a3 = attribute.a3,
                                          a4 = attribute.a4
                                  } into t
                                  group t by t.product.id into g
                                     select new
                                     {
                                         product = g.Select(p => p.product).FirstOrDefault(),
                                         attributes = g.Select(r => new Attr()
                                         {
                                             a1 = r.a1,
                                             a2 = r.a2,
                                             a3 = r.a3,
                                             a4 = r.a4
                                         }).ToList()
                                     }
                                    ).ToList();

但这花了大约 70 分钟,当我查看它生成的 SQL 查询时,我看到了数十个子查询和数十个连接。

然后我尝试在 sql server 上进行分组,并在应用程序服务器上投影到所需的结构中。这是 EF 代码:

    var productsWithAttributes = (from product in ctx.products
                                  join attribute in ctx.attributes on product.id equals attribute.productId
                                  select new
                                      {
                                          product = product,
                                          a1 = attribute.a1,
                                          a2 = attribute.a2,
                                          a3 = attribute.a3,
                                          a4 = attribute.a4
                                  } into t
                                      group t by t.product.id

                            ).ToList();

这大约需要 3 分钟。但是这个查询产生的 SQL 看起来仍然很复杂,有多个子查询和连接。我会期待以下内容:

select product.*, attribute.a1, attribute.a2, attribute.a3, attribute.a4
from product
join attribute on product.id = attribute.productId
group by product.id

然后我尝试了只加入而不分组:

    var productsWithAttributes = (from product in ctx.products
                                      join attribute in ctx.attributes on product.id equals attribute.productId
                                      select new
                                      {
                                          product = product,
                                          a1 = attribute.a1,
                                          a2 = attribute.a2,
                                          a3 = attribute.a3,
                                          a4 = attribute.a4,

                                      }
                                    ).ToList();

这花了 1.5 分钟,EF 生成的 SQL 代码符合预期。

简而言之,将分组添加到连接会创建一个复杂的 SQL 查询,它需要更长的时间,但在性能方面仍然可以接受。但是在这个分组之后添加最终的投影会产生一个非常复杂的 SQL 查询,它会花费大量的时间并且无法接受。

使用 EF 创建此查询的正确方法是什么?

【问题讨论】:

  • 问题在于select product.*, attribute.a1, attribute.a2, attribute.a3, attribute.a4group by product.id 的组合不是有效的SQL(至少对于大多数主要的关系数据库而言)。其次,所需的结果形状对于 SQL 来说是不自然的。但是,为什么您需要joingroup by - 具有适当的实体导航属性,查询应该是简单的投影(select),具有相对简单的SQL 翻译?你的Product 类中没有类似public ICollection<Attribute> Attributes { get; set; } 的东西吗?
  • @IvanStoev 不幸的是,数据库不包含关系。
  • 我明白了。这是什么 EF - 6,Core(版本)?它是执行单个 SQL 查询还是 N + 1 个 SQL 查询?您能否分享查询中使用的类以及上述信息?目前的信息不足以给你一个好的建议。
  • @hakanviv 即使数据库不包含外键,您也可以定义导航属性。如前所述,这将使生活变得更加轻松。你可以做类似product.Attributes 的事情。同时,您应该想知道为什么没有 FK。考虑添加它们。

标签: c# entity-framework


【解决方案1】:

如果您想创建连接表,那么您所要做的就是创建另一个带有 pk(主键)的表并完全连接它们,而不是内部连接它们或只是 joinig。

【讨论】:

    【解决方案2】:

    在 LINQ to Entities 中创建此类查询的推荐方法是使用集合导航属性,或者在它缺失的情况下 - Group Join 构造 (join ... into):

    组连接产生一个分层结果序列,它将左侧源序列中的元素与右侧源序列中的一个或多个匹配元素相关联。组连接在关系方面没有等价物;它本质上是一个对象数组序列。

    类似这样的:

    var productsWithAttributes = (
        from product in ctx.products
        join attribute in ctx.attributes on product.id equals attribute.productId
        into attributes // <-- emulate product.attributes property
        select new
        {
            product = product,
            attributes = attributes.Select(attribute => new Attr()
            {
                a1 = attribute.a1,
                a2 = attribute.a2,
                a3 = attribute.a3,
                a4 = attribute.a4
             }).ToList(),
         }).ToList();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-21
      • 1970-01-01
      • 2023-04-06
      • 2019-05-03
      • 1970-01-01
      • 2010-11-18
      相关资源
      最近更新 更多