如何使用单个查询进行左连接,分组和选择?

时间:2017-08-20 07:35:50

标签: c# linq

假设我有以下数据:

var workers = new[]
{
    new {  Name = "John",  Id = 1 },
    new {  Name = "Greg",  Id = 2 }, 
    new {  Name = "Jack",  Id = 3 }, 
    new {  Name = "Josh",  Id = 4 },
    new {  Name = "Jill",  Id = 5 },
    new {  Name = "Jane",  Id = 6 }
};

var contracts = new[]
{
    new {  ContractNumber="1",  WorkerId=1, ContractDate = new DateTime(2017,6,30) },
    new {  ContractNumber="2",  WorkerId=2, ContractDate = new DateTime(2017,7,10) },
    new {  ContractNumber="3",  WorkerId=2, ContractDate = new DateTime(2017,7,15) },
    new {  ContractNumber="4",  WorkerId=5, ContractDate = new DateTime(2017,7,20) },
    new {  ContractNumber="5",  WorkerId=1, ContractDate = new DateTime(2017,7,25) }
};

我需要做的是选择合约日期大于或等于的最低合约数量的第一名工人:

var fromDate = new DateTime(2017, 7, 1);

排除具有以下ID的工人:

int[] exceptWorkerIds = new int[] {1, 4};

如果几个工人的合同最小数量相似,那么请按字母顺序选择名字第一名的工人。

我按照以下方式解决了这个问题。

首先,为每个工人留下加入合同。如果合同存在,我的帮助属性ContractExists = 1,如果不存在则为0.

var query = 
from w in workers.Where(x => !exceptWorkerIds.Contains(x.Id))
join c in contracts.Where(x => x.ContractDate >= fromDate) 
    on w.Id equals c.WorkerId into workerContracts
from wc in workerContracts.DefaultIfEmpty()
select new {WorkerId = w.Id, WorkerName = w.Name, ContractExists = wc == null ? 0: 1};

此查询给出了以下结果:

enter image description here

其次,我将获得的结果分组为WorkerId,WorkerName通过总和和工人名称获得合同和订单数据的总和:

var result = 
(from q in query
group q.ContractExists by new {q.WorkerId, q.WorkerName} into g
orderby g.Sum(), g.Key.WorkerName
select new 
{
    WorkerId = g.Key.WorkerId, 
    WorkerName = g.Key.WorkerName, 
    WorkerContractsCount = g.Sum()
}).ToList().Take(1);

enter image description here

Take(1)给出了结果数据的前1位:

enter image description here

问题:有没有办法用唯一的查询或任何更简单或优雅的方式来做到这一点?如果是,这是否有助于提高查询执行的效率?

2 个答案:

答案 0 :(得分:2)

您可以使用group join(实际上您的查询之前使用的内容{而不是 join (将数据相乘),然后执行 group by {1}})。

另一个逻辑几乎相同 - from wc in workerContracts.DefaultIfEmpty()为您提供所需的合约数量,因此请先应用所需的订单,然后完成第一步:

workerContracts.Count()

答案 1 :(得分:2)

可能比import { Supplier } from './supplier.model'; import { Rating } from './rating.model'; export class Product { constructor( public productId?: number, public name?: string, public category?: string, public description?: string, public price?: number, public supplier?: Supplier, public ratings?: Rating[]) { } } Where更短Join,这是一个更紧凑的版本:

var result = workers
    .Where(w => !exceptWorkerIds.Contains(w.Id))
    .Select(w => new { 
        Name = w.Name, 
        Id = w.Id, 
        Nb = contracts
            .Count(c => c.WorkerId == w.Id && c.ContractDate >= new DateTime(2017,7,1))
    })
    .OrderBy(w => w.Nb).ThenBy(w => w.Name).FirstOrDefault();

if(result != null)
    Console.WriteLine(result.Name);
else
    Console.WriteLine("Result not found");

说明:对于除我们不想检查的工人以外的每个工人,我们计算相关合同的数量,该日期晚于或等于2017,7,1,然后我们按照这个数字和名称对其进行排序,并采取第一个。