SQL - 基于其他表的值连接/查找表,并进行范围比较

时间:2016-11-01 10:38:17

标签: sql sql-server

我将通过比较日期值和“更大”或“小于”条件来查询3个表。但我没有写一个关于这个的SQL。日期比较表只有一个字段而不是两个日期的范围,即开始日期和结束日期。表格是:

staff_roster - 包含员工每月记录的主要表格:

staff_no | store |  r_date
----------------------------
sf001    |   A   | 1/10/2016
sf001    |   A   | 2/10/2016
sf001    |   A   | 3/10/2016
sf001    |   A   | 4/10/2016
sf001    |   A   | 5/10/2016
sf001    |   A   | 6/10/2016
sf001    |   A   | 7/10/2016
sf001    |   A   | 8/10/2016
  ....
sf002    |   A   | 1/10/2016
sf002    |   A   | 2/10/2016
  ....

staff_posmove - 条件查找的表:

staff_no |    p_date    | previous_pos | new_pos
---------------------------------------------------
sf001    |  3/10/2016   | Jr sales     | clerk
sf001    |  6/10/2016   | clerk        | Sr sales

staff_profile

staff_no | position 
---------------------
sf001    | Sr sales 
sf002    | Jr sales 

我想得到一个结果,其中包含表“staff_roster”的列,并附加一列“position”,其值取决于其他2个表。

逻辑是:它将每个staff_roster.r_date与表“staff_posmove”的“p_date”进行比较。如果系统发现“r_date”小于“p_date”的相同人员,则“position”的值为“previous_pos”;但如果它等于或大于,则将“new_pos”作为值。

但问题是“staff_posmove”中可能有多条记录,这会产生重复和错误的结果,因为显然存在重叠条件。

以sf001为例,我的预期结果为:

staff_no | store |   r_date  | position
-----------------------------------------
sf001    |   A   | 1/10/2016 | Jr sales
sf001    |   A   | 2/10/2016 | Jr sales
sf001    |   A   | 3/10/2016 | clerk
sf001    |   A   | 4/10/2016 | clerk
sf001    |   A   | 5/10/2016 | clerk
sf001    |   A   | 6/10/2016 | Sr sales
sf001    |   A   | 7/10/2016 | Sr sales
sf001    |   A   | 8/10/2016 | Sr sales
 ....

此外,如果系统没有在员工的“staff_posmove”中找到记录进行比较,“位置”应该查找表“staff_profile”,将相关员工的位置放在结果“位置”中:

staff_no | store |   r_date  | position
-----------------------------------------
sf002    |   A   | 1/10/2016 | Jr sales
sf002    |   A   | 2/10/2016 | Jr sales
 ....

我尝试了类似“CASE WHEN a.staff_no = b.staff_no AND a.r_date< b.p_date THEN CAST(b.previous_pos As varchar(10))”但仍然得到错误的结果。问题可能有点长,但希望你能提供帮助。

2 个答案:

答案 0 :(得分:1)

解决此问题的典型方法是使用OUTER APPLY。这变得更难,因为您没有第一个记录 - staff_posmove缺少初始记录。

要解决此问题,以下内容始终考虑每个职员的第一条记录,然后使用select中的逻辑来选择职位:

select r.*,
       (case when pm.p_date < r_date then pm.new_pos else pm.old_pos end) as position
from staff_roster r outer apply
     (select top 1 pm.*
      from (select pm.*,
                   row_number() over (partition by staff_no order by p_date) as seqnum
            from staff_posmove pm
           ) pm
      where p_date < r_date or seqnum = 1  -- always ensure first row is considered
      order by pm.p_date desc
     ) pm

答案 1 :(得分:0)

未经测试:

以下内容应该是每个名单之后的第一步(因此我们接受上一份工作):

Select
r.staff_no, r.store_, r.r_date, p.previous_job
from staff_roster r left join staff_posmove p 
on p.staff_no = r.staff_no and r_date < p_date 
where row_number() over (partition by r.staff_no, r_date order by p_date) =1

我们将staff_profile添加到其中以获取剩余的那些:

Select
p.staff_no, r.store_, r.r_date, nvl(m.previous_job,p.position)
from staff_profile p join staff_roster r
on p.staff_no = r.staff_no
left join staff_posmove m
on m.staff_no = r.staff_no and r.r_date < m.p_date 
where row_number() over (partition by r.staff_no, r.r_date order by m.p_date) =1