C# 对联接表进行分组
我正在尝试连接产品和属性表,并按产品id对它们进行分组,以便获得具有其属性的产品列表。因此,我尝试了以下实体框架查询:C# 对联接表进行分组,c#,entity-framework,C#,Entity Framework,我正在尝试连接产品和属性表,并按产品id对它们进行分组,以便获得具有其属性的产品列表。因此,我尝试了以下实体框架查询: var productsWithAttributes = (from product in ctx.products join attribute in ctx.attributes on product.id equals attribute.productId
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创建此查询的正确方法是什么?如果要创建联接表,则只需创建另一个同时具有pk(主键)和完全联接的表,而不是内部联接或只是联接。在LINQ to实体中创建此类查询的推荐方法是使用集合导航属性,或者,如果缺少-构造(
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();
var产品属性=(
来自ctx.products中的product
product.id上的ctx.attributes中的join属性等于attribute.productId
进入属性//新建属性()
{
a1=属性a1,
a2=属性A.a2,
a3=属性a3,
a4=属性A.a4
}).ToList(),
}).ToList();
问题在于,选择product.*、attribute.a1、attribute.a2、attribute.a3、attribute.a4
与按产品分组.id
组合使用不是有效的SQL(至少对于大多数主要的关系数据库是如此)。其次,SQL所需的结果形状不是自然的。但是为什么您需要join
和groupby
——有了适当的实体导航属性,查询应该是简单的投影(select
)和相对简单的SQL转换?您的产品
类中没有类似于公共ICollection Attributes{get;set;}
的内容吗?@IvanStoev不幸的是,数据库不包含关系。我明白了。这是什么EF-6,核心(版本)?它是执行单个SQL查询还是N+1 SQL查询?您能否共享查询中使用的类以及上述信息?当前信息不足以给您一个好的建议。@hakanviv即使数据库不包含外键,您也可以定义导航属性。如前所述,这将使生活更加轻松。您可以执行类似于product.Attributes
的操作。同时,您应该想知道为什么没有FK。考虑添加它们。