过滤LEFT JOINed表,其中包含日期以显示当前事件,否则将来还会过去?

时间:2014-03-09 21:56:34

标签: sql sql-server-2008

我有一张表,列出了不同用户的假期信息(用户名,假期开始和假期结束日期) - 下面列出了4位用户:

Username    VacationStart   DeploymentEnd
rsuarez     2014-03-10      2014-03-26
studd       2014-01-18      2014-01-29
studd       2014-02-11      2014-02-26
studd       2014-03-02      2014-03-04
ssteele     2014-03-11      2014-03-26
ssteele     2014-03-18      2014-03-28
atidball    2014-03-05      2014-03-20
atidball    2014-03-06      2014-03-26
atidball    2014-03-13      2014-03-20
atidball    2014-03-18      2014-03-31

对于新查询,我想只显示4行,每个用户只显示一组休假日期,当前/进行中休假,未来/下一个休假(如果没有当前存在)或最近(如果以上两个都是假的。)

最终结果应该是(假设今天是2014年3月9日):

Username    VacationStart   DeploymentEnd
rsuarez     2014-03-10      2014-03-26
studd       2014-03-02      2014-03-04
ssteele     2014-03-11      2014-03-26
atidball    2014-03-05      2014-03-20

假期日期实际上来自另一个表(data_vacations),我将其连接到data_users。我试图在左连接语句中执行大小写选择。

这是我之前尝试过的,但我的逻辑在那里失败了,因为我最终将不同的假期结束日期与假期开始日期混合:

SELECT Username, VacationStart, VacationEnd
FROM data_users
LEFT JOIN  
(
    SELECT userGUID, 
    CASE WHEN MIN(CASE WHEN (VacationEnd < getdate()) THEN NULL ELSE VacationStart END) IS NULL THEN MAX(VacationStart) 
    ELSE MIN(VacationStart) END AS VacationStart,  


    CASE WHEN MIN(CASE WHEN (VacationEnd < getdate()) THEN NULL ELSE VacationEnd END) IS NULL THEN MAX(VacationEnd) 
    ELSE MIN(VacationEnd) END AS VacationEnd 


    FROM data_vacations
    GROUP BY userGUID
) b ON(data_empl_master.userGUID= b.userGUID) 

我做错了什么?我怎么能解决它?

另外..旁边注意..我是否正确地在LEFT JOIN中执行此过滤?由于data_users更大,具有不同的用户ID ...我想基于上面的示例加入可用的度假信息,同时仍然显示所有唯一的用户ID。

3 个答案:

答案 0 :(得分:0)

select u.name,s.startdate,s.enddate 
from users u
left join 
(
      select su.name,
             max(su.start) as startdate,
             max(su.end) as enddate from users su group by su.name
)s on u.name= s.name
group by u.name 

答案 1 :(得分:0)

既然你问了两个问题,我会回答一个关于获得假期的问题,并让你弄清楚加入。

我认为您不能在一个简单的查询中获得所需的假期日期。首先,您需要确定给定的日期范围是过去,现在还是将来。然后,您需要按开始/结束日期对这些范围进行排序,以获得最新或即将到来的日期。您需要按降序排序过去的假期并按升序排序。足够有趣的用户atidball有两个正在进行的假期,我以与未来假期相同的方式对其进行排序。最后应用你的规则,我按状态排序。

declare @currentDate date = '20140309'   
;
with cte1 as
(
  -- state: the lower number the higher priority
  select Username, VacationStart, DeploymentEnd,
    case 
      when VacationStart <= @currentDate and DeploymentEnd >= @currentDate
        then 0 -- in progress
      when VacationStart > @currentDate
        then 1 -- future
      when DeploymentEnd < @currentDate
        then 2 -- past
      else NULL
    end as state
  from data_vacations
)
, cte2 as
(
  select *, 
    row_number() over(partition by username, state order by VacationStart, DeploymentEnd) as rn
  from cte1
  where state < 2 -- current or upcoming

  union all

  select *, 
    row_number() over(partition by username, state order by DeploymentEnd desc, VacationStart desc) as rn
  from cte1
  where state = 2 -- past
)
, cte3 as
(
  -- apply the rules: find the record with highest priority
  select Username, min(state) as minstate
  from cte1
  group by Username
)
select cte2.Username, cte2.VacationStart, cte2.DeploymentEnd
from cte2
inner join cte3
  on cte2.Username = cte3.Username
  and cte2.state = cte3.minstate
  and cte2.rn = 1  -- most recent or next upcoming

请参阅SQLFiddle

答案 2 :(得分:0)

使用公用表表达式按类别排序(current = 1,future = 2,past = 3),每个类别按开始日期/差异从GETDATE()开始,您可以通过对结果进行排名来获得所需的结果使用ROW_NUMBER();

DECLARE @DATE DATETIME = GETDATE()

;WITH cte AS (
  SELECT *, 1 r, VacationStart s FROM data_users 
  WHERE @DATE BETWEEN VacationStart and DeploymentEnd
  UNION ALL
  SELECT *,2 r, VacationStart - @DATE s FROM data_users 
  WHERE VacationStart > @DATE
  UNION ALL
  SELECT *,3 r, @DATE - DeploymentEnd s FROM data_users 
  WHERE DeploymentEnd < @DATE
), cte2 AS (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY username ORDER BY r,s) rn FROM cte
)
SELECT Username, VacationStart, DeploymentEnd FROM cte2 WHERE rn=1;

An SQLfiddle to test with

获取日期作为变量是必要的,以便在整个查询中获得一致的GETDATE()值,否则如果多次调用它可能不一致。