简体   繁体   English

分组并左加入 linq

[英]Group by and left join in linq

There are two tables, one is customers has the fields customerID , GroupID and the other one is CustomerGroup has the fields GroupID , GroupName , I want to get the quantity of customerID in each group, here is the LINQ statement:有两张表,一张是customers,有customerIDGroupID ,一张是CustomerGroup ,有GroupIDGroupName ,我想得到每组customerID的数量,这里是LINQ语句:

var groups = from customerGroups in db.CustomerGroup 
                         join customers in db.Customers on customerGroups.GroupID equals customers.GroupID into gc
                         where customerGroups.MerchantID == merchantID
                         from subCustomerGroups in gc.DefaultIfEmpty()
                         group customerGroups by customerGroups.GroupName into grpCustomerGroups
                         select new { GroupName = grpCustomerGroups.Key, Quantity = customers.Count()};

the problme is that Quantity = customers.Count() is invalid, how to correct the statement?问题是Quantity = customers.Count()无效,如何更正该语句? The expected sql steatment is预期的 sql 声明是

exec sp_executesql N'SELECT 
    1 AS [C1], 
    [GroupBy1].[K1] AS [GroupName], 
    [GroupBy1].[A1] AS [C2]
    FROM ( SELECT 
        [Extent1].[GroupName] AS [K1], 
        COUNT(CustomerID) AS [A1]
        FROM  [dbo].[CustomerGroup] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Customer] AS [Extent2] ON [Extent1].[GroupID] = [Extent2].[GroupID]
        WHERE [Extent1].[MerchantID] = @p__linq__0
        GROUP BY [Extent1].[GroupName]
    )  AS [GroupBy1]',N'@p__linq__0 bigint',@p__linq__0=9

Usually, if you find yourself doing a left outer join followed by a GroupBy, it is because you want "items with their sub-items", Like "Schools with their Students", "Clients with their Orders", "CustomerGroups with their Customers", etc. If you want this, consider using GroupJoin instead of "Join + DefaultIfEmpty + GroupBy"通常,如果您发现自己在进行 GroupBy 后的左外连接,那是因为您想要“项目及其子项目”,例如“学校及其学生”、“客户及其订单”、“客户组及其客户” ”等。如果需要,请考虑使用GroupJoin而不是“Join + DefaultIfEmpty + GroupBy”

I'm more familiar with method syntax, so I'll use that one.我更熟悉方法语法,所以我会使用那个。

int merchantId = ...
var result = dbContext.CustomerGroups

    // keep only the CustomerGroups from merchantId
    .Where(customerGroup => customerGroup.MerchantId == merchantId)

    .GroupJoin(dbContext.Customers,            // GroupJoin with Customers
    customerGroup => customerGroup.GroupId,    // from every CustomerGroup take the GroupId
    customer => customer.GroupId,              // from every Customer take the GroupId

    // ResultSelector:
    (customerGroup, customersInThisGroup) => new  // from every CustomerGroup with all its
    {                                             // matching customers make one new object
        GroupName = customerGroup.Key,
        Quantity = customersInThisGroup.CustomerId,  // ???
    });

In words:用一句话来说:

Take the sequence of CustomerGroups.采用 CustomerGroups 的序列。 Keep only those CustomerGroups that have a value for property MerchantId equal to merchantId.仅保留属性 MerchantId 的值等于 MerchantId 的客户组。 From every remaining CustomerGroup, get all its Customers, by comparing the CustomerGroup.GroupId with each Customer.GroupId.通过将 CustomerGroup.GroupId 与每个 Customer.GroupId 进行比较,从每个剩余的 CustomerGroup 中获取其所有客户。

The result is a sequence of CustomerGroups, each with its Customers.结果是一系列 CustomerGroup,每个都有自己的客户。 From this result (parameter ResultSelector) get the GroupName from the Customer and the Quantity from the Customers in this group.从此结果(参数 ResultSelector)中获取来自客户的 GroupName 和来自该组中客户的数量。

Your statement was:你的陈述是:

Quantity = customers.CustomerID,

This will not work.这行不通。 I'm sure this is not what you want.我确定这不是你想要的。 Alas you forgot to write what you want.唉,你忘了写你想要的。 I think it is this:我认为是这样的:

Quantity = customers.Count().

But if you want the CustomerId of all Customers in this CustomerGroup:但是,如果您想要此 CustomerGroup 中所有客户的 CustomerId:

// ResultSelector:
(customerGroup, customersInThisGroup) => new
{                                           
    GroupName = customerGroup.Key,
    CustomerIds = customersInThisGroup.Select(customer => customer.CustomerId)
                                      .ToList(),
);

If you want you can use the ResultSelector to get "CustomerGroups with their Customers".如果您愿意,可以使用 ResultSelector 来获取“CustomerGroups with their Customers”。 Most efficient is to select only the properties you actually plan to use:最有效的是 select 仅使用您实际计划使用的属性:

// ResultSelector:
(customerGroup, customersInThisGroup) => new
{      
    // select only the CustomerGroup properties that you plan to use:
    Id = CustomerGroup.GroupId,
    Name = CustomerGroup.Name,
    ... // other properties that you plan to use

    Customers = customersInThisGroup.Select(customer => new
    {
         // again, select only the Customer properties that you plan to use
         Id = customer.Id,
         Name = customer.Name,
         ...

         // not needed, you know the value:
         // GroupId = customer.GroupId
    });

The reason not to select the foreign key of the Customers, is efficiency.不给客户的外键 select 的原因是效率。 If CustomerGroup [14] has 1000 Customers, then every Customer in this group will have a value for GroupId equal to [14].如果 CustomerGroup [14] 有 1000 个客户,则该组中的每个客户的 GroupId 值将等于 [14]。 It would be a waste to send this value [14] 1001 times.发送此值 [14] 1001 次将是一种浪费。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM