【发布时间】:2017-08-20 07:35:49
【问题描述】:
假设我有以下数据:
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};
这个查询给了我以下结果:
其次,我将得到的结果按WorkerId分组,WorkerName通过sum和worker name得到合同和订单数据的总和:
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);
Take(1) 给了我结果数据的前 1 个:
问题:有没有办法用唯一的查询或任何比我更简单或优雅的方式来做到这一点?如果是,这是否有助于提高查询执行的效率?
【问题讨论】:
-
workers.FirstOrDefault(w => !contracts.Any(c => c.WorkerId == w.Id))会给你第一个没有任何合同的工人。如果为 null,则可以使用内部连接找到您搜索的工作人员。不是答案,因为它会导致 2 个查询,而不仅仅是 1 个,但仍然 :) -
有趣。如果不为null,则不需要执行第二个查询。
-
是的,在您的示例中,它将更快地为您提供 Jack。