我有一个自我引用的雇员表,这意味着有些雇员的主键“ EmployeeID”和父键(主管)的外键“ SupervisorID”。管理者位于层次结构中,因此其SupervisorID为null。还有一个日期列(CreatedOn),用于过滤特定日期,但只有经理有日期,而没有员工。 我正在尝试从特定日期,经理及其雇员那里获得所有结果,但我只是无法运行它。我需要使用LINQ来实现这一点,但我想我可以首先进行SQL查询,以查看是否能够以某种方式将其迁移到LINQ查询(与哪种语法无关)。但是,一旦我意识到这和LINQ一样困难,这真是错觉。 我以为可以通过CTE来实现,但是我根本无法运行自引用。我只是得到管理者,还是一无所有... 这是一个示例表:
------------------------------------------------------
|EmployeeID|Name |SupervisorID|CreatedOn |
|1 |Sanders |NULL |2019-01-01 16:20:33|
|2 |Flanders|1 |NULL |
|3 |Andrews |1 |NULL |
|4 |Ranger |NULL |2019-03-20 18:04:56|
|5 |Hammer |4 |NULL |
|6 |Enderson|4 |NULL |
|7 |Larsson |NULL |2019-10-03 04:55:16|
|8 |Simpson |8 |NULL |
------------------------------------------------------
您可以看到,桑德斯(2019-01-01 16:20:33)有2名员工(佛兰德斯,安德鲁斯),游侠2019-03-20 18:04:56有2名员工(汉默,恩德森)和Larsson(2019-10-03 04:55:16)拥有1名员工(Simpson)
使用LINQ我一无所获。如果我按日期进行过滤,则没有任何办法可以对员工的初始数据集进行过滤。
// This filters by date, but then I can't get the employees without date anymore
employees.Where(e => e.CreatedOn >= '2019-01-01' && e.CreatedOn < '2019-01-02')
// This filters all employees with a parent, but then I can't get the managers anymore and can't filter against a date anymore either
employees.Where(e => e.SupervisorID != null)
我尝试了加入,但效果也不佳:
from employees m in employees
join e in employees
on m.EmployeeId equals e.SupervisorID
where m.CreatedOn >= '2019-01-01' && m.CreatedOn < '2019-01-02'
select m
自从1月1日创建桑德斯公司入职以来,由于桑德斯及其员工在他的领导下工作,因此预期的合适过滤条件将是桑德斯及其员工。
------------------------------------------------------
|EmployeeID|Name |SupervisorID|CreatedOn |
|1 |Sanders |NULL |2019-01-01 16:20:33|
|2 |Flanders|1 |NULL |
|3 |Andrews |1 |NULL |
------------------------------------------------------
有人知道吗,我怎么能做到这一点?我将无法调整数据库设计,因为我只能读取,而另一个应用程序可以推送数据,而我无法更改。
答案 0 :(得分:1)
让我们分别看一下主管和员工,然后再将两者合并。
主管
对于主管来说,逻辑很简单,您可以根据创建日期进行过滤。
var fromDate = new DateTime(2019,1,1);
var untilDate = new DateTime(2019,1,2);
var supervisors = employees
.Where(e => e.CreatedOn >= fromDate
&& e.CreatedOn < untilDate )
.ToList();
注意:在您的数据集中,只有主管具有创建日期。如果员工也可以有创建日期,则需要显式过滤掉他们:
var supervisors = employees
.Where(e => e.CreatedOn >= fromDate
&& e.CreatedOn < untilDate
&& e.SupervisorID == null)
.ToList();
员工
员工的逻辑稍微复杂一些,但是您可以依靠EF的导航属性来为您处理联接。
简而言之,您希望主管的创建日期在所选范围内的员工。
var fromDate = new DateTime(2019,1,1);
var untilDate = new DateTime(2019,1,2);
var subordinates = employees
.Where(e => e.SupervisorID != null
&& e.Supervisor.CreatedOn >= fromDate
&& e.Supervisor.CreatedOn < untilDate )
.ToList();
合并两者
合并这些内容非常简单,实际上是在supervisorFilter OR subordinateFilter
中完成
var fromDate = new DateTime(2019,1,1);
var untilDate = new DateTime(2019,1,2);
var supervisorsAndSubordinates = employees
.Where(e =>
// Supervisor filter
(
e.CreatedOn >= fromDate
&& e.CreatedOn < untilDate
)
||
// Subordinate filter
(
e.SupervisorID != null
&& e.Supervisor.CreatedOn >= fromDate
&& e.Supervisor.CreatedOn < untilDate
))
.ToList();
这将为您提供期望的结果集。请注意,当此结构是递归结构(即多个“主管的主管”)时,解决方案将变得更加困难,但是您的示例当前并非如此。
答案 1 :(得分:0)
使用加入,您可以同时拥有经理及其下属:
var list = employees.Where(m=> m.CreatedOn >= new DateTime(2019,1,1) && m.CreatedOn < new DateTime(2019,1,2)).Join(employees,sc=> sc.EmployeeId, soc=> soc.SupervisorId, (sc,soc)=> new {left= sc, right=soc} ).ToList();
我创建了一个小提琴,请检查以下链接:
注意:我与员工一起加入了自己,考虑了左侧列表的日期过滤器,并加入了右侧列表的supervisorId。它显示了经理及其各自的员工
答案 2 :(得分:0)
您需要一个left outer join
类似于
var mylist = (from employee in employees
join r in employees on employee.SupervisorId equals r.EmployeeId into results
from supervisor in results.DefaultIfEmpty()
let createdDate = supervisor != null ? supervisor.CreatedOn : employee.CreatedOn
where createdDate >= new DateTime(2019,1,1) && createdDate < new DateTime(2019,1,2)
select employee);
我假设只有两个级别(即没有人可以担任主管和拥有主管)。
答案 3 :(得分:-1)
所有Linq方法都是惰性求值的,您应该使用终端方法立即获得结果。
终端方法,例如ToList,ToArray,ForEach ...
employees.Where(e => e.SupervisorID != null).ToList()