SQL - 这个SELECT CASE的最佳工作方式是什么?

时间:2014-02-04 14:36:06

标签: sql select case grouping

我有一张表出席和一个活动类型表

我想每月将每个人的活动分组在不同的栏目中

示例表:

考勤

人员ID
日期
GateIn
GateOut
ActivityID

活动

ID
名称

值为Activity.Name为:Present,Absence,Ill和Holiday

我希望获得一个视图(按人员ID和月份分组

是PersonID


没有
假日
生病
LessThan4Hours
MoreThan4Hours
MoreThan5Hours

创建此分组的最佳方法是什么? 我已经有两个SQL语句,但两者似乎都太不友好了

谁能帮助我? 感谢

    SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, COUNT(1) as Absence, 0 as Holiday, 0 as Ill, 0 as LessThan4Hours, 0 as MoreThan4Hours, 0 as MoreThan5Hours
    FROM Attendance att
    inner join Activities act on att.ActivityID = act.ID
    WHERE act.Name = 'Absence'
    GROUP BY year(att.Date), month(att.Date), att.PersonID
    UNION
    SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, COUNT(1) as Holiday, 0 as Ill, 0 as LessThan4Hours, 0 as MoreThan4Hours, 0 as MoreThan5Hours
    FROM Attendance att
    inner join Activities act on att.ActivityID = act.ID
    WHERE act.Name = 'Holiday'
    GROUP BY year(att.Date), month(att.Date), att.PersonID
    UNION
    SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, 0 as Holiday, count(1) as Ill, 0 as LessThan4Hours, 0 as MoreThan4Hours, 0 as MoreThan5Hours
    FROM Attendance att
    inner join Activities act on att.ActivityID = act.ID
    WHERE act.Name = 'Ill'
    GROUP BY year(att.Date), month(att.Date), att.PersonID
    UNION
    SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, 0 as Holiday, 0 as Ill, COUNT(1) as LessThan4Hours, 0 as MoreThan4Hours, 0 as MoreThan5Hours
    FROM Attendance att
    inner join Activities act on att.ActivityID = act.ID
    WHERE att.GateOut is not null
    AND (DATEDIFF(n, att.GateIn, att.GateOut)/60) <= 4
    AND act.Name = 'Present'
    GROUP BY year(att.Date), month(att.Date), att.PersonID
    UNION
    SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, 0 as Holiday, 0 as Ill, 0 as LessThan4Hours, COUNT(1) as MoreThan4Hours, 0 as MoreThan5Hours
    FROM Attendance att
    inner join Activities act on att.ActivityID = act.ID
    WHERE att.GateOut is not null
    AND (DATEDIFF(n, att.GateIn, att.GateOut)/60) > 4
    AND (DATEDIFF(n, att.GateIn, att.GateOut)/60) <= 5
    AND act.Name = 'Present'
    GROUP BY year(att.Date), month(att.Date), att.PersonID
    UNION
    SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, 0 as Absence, 0 as Holiday, 0 as Ill, 0 as LessThan4Hours, 0 as MoreThan4Hours, COUNT(1) as MoreThan5Hours
    FROM Attendance att
    inner join Activities act on att.ActivityID = act.ID
    WHERE att.GateOut is not null
    AND (DATEDIFF(n, att.GateIn, att.GateOut)/60) > 5
    AND act.Name = 'Present'
    GROUP BY year(att.Date), month(att.Date), att.PersonID

我也在SELECT CASE中解决了这个问题,可能还会更糟?

    SELECT a.PersonID, a.cYear, a.cMonth, SUM(a.Absence) AS Absence, SUM(a.Holiday) AS Holiday, SUM(a.Ill) AS Ill, SUM(a.LessThan4Hours) AS LessThan4Hours, SUM(a.MoreThan4Hours) AS MoreThan4Hours, SUM(a.MoreThan5Hours) AS MoreThan5Hours
    FROM 
    (
    SELECT year(att.Date) as cYear, month(att.Date) as cMonth, att.PersonID, CASE 
        WHEN act.Name = 'Ill' THEN 1
        WHEN act.Name = 'Holiday' THEN 0
        WHEN act.Name = 'Absence' THEN 0
        WHEN GateOut = null THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
        ELSE 0 
      END AS Ill,
       CASE 
        WHEN act.Name = 'Ill' THEN 0
        WHEN act.Name = 'Holiday' THEN 1
        WHEN act.Name = 'Absence' THEN 0
        WHEN GateOut = null THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
        ELSE 0 
      END AS Holiday,
      CASE 
        WHEN act.Name = 'Ill' THEN 0
        WHEN act.Name = 'Holiday' THEN 0
        WHEN act.Name = 'Absence' THEN 1
        WHEN GateOut = null THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
        ELSE 0 
      END AS Absence,
      CASE 
        WHEN act.Name = 'Ill' THEN 0
        WHEN act.Name = 'Holiday' THEN 0
        WHEN act.Name = 'Absence' THEN 0
        WHEN GateOut = null THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 1
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
        ELSE 0
      END AS MoreThan5Hours,
      CASE 
        WHEN act.Name = 'Ill' THEN 0
        WHEN act.Name = 'Holiday' THEN 0
        WHEN act.Name = 'Absence' THEN 0
        WHEN GateOut = null THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 1
        WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 0
        ELSE 0 
      END AS MoreThan4Hours,
      CASE 
        WHEN act.Name = 'Ill' THEN 0
        WHEN act.Name = 'Holiday' THEN 0
        WHEN act.Name = 'Absence' THEN 0
        WHEN GateOut = null THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 5 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) > 4 THEN 0
        WHEN (DATEDIFF(n, gatein, gateout)/60) <= 4 THEN 1
        ELSE 0 
      END AS LessThan4Hours
  from Attendance att
  inner join Activities act on att.ActivityID = act.ID 
  ) a
  GROUP BY a.PersonID, a.cYear, a.cMonth
  ORDER BY a.cYear DESC, a.cMonth DESC, a.PersonID

2 个答案:

答案 0 :(得分:1)

这可能更容易阅读和遵循。内部“PreChk”查询遍历记录,只是将它们标记为不同的类型和小时,然后应用按您的要求分组的摘要。

SELECT
      PreChk.PersonID, 
      PreChk.cYear, 
      PreChk.cMonth,
      SUM(PreChk.Absence) AS Absence, 
      SUM(PreChk.Holiday) AS Holiday, 
      SUM(PreChk.Ill) AS Ill, 
      SUM( case when PreChk.HrsDif > 5 then 1 else 0 end ) as MoreThan5Hours,
      SUM( case when PreChk.HrsDif > 4 AND PreChk.HrsDif < 5 then 1 else 0 end ) as MoreThan4Hours,
      SUM( case when PreChk.HrsDif < 4 then 1 else 0 end ) as LessThan4Hours
   from 
      ( SELECT 
              att.PersonID, 
              year(att.Date) as cYear, 
              month(att.Date) as cMonth, 
              CASE WHEN act.Name = 'Ill' THEN 1 else 0 end as Ill,
              CASE WHEN act.Name = 'Holiday' THEN 1 else 0 end as Holiday,
              CASE WHEN act.Name = 'Absence' THEN 1 else 0 end as Absence,
              CASE when GateOut = null 
                   THEN 0
                   ELSE DATEDIFF(n, gatein, gateout)/60) end as HrsDif
           from 
              Attendance att
                 inner join Activities act 
                    on att.ActivityID = act.ID ) PreChk
   GROUP BY 
      PreChk.PersonID, 
      PreChk.cYear, 
      PreChk.cMonth
   ORDER BY 
      PreChk.cYear DESC, 
      PreChk.cMonth DESC, 
      PreChk.PersonID

答案 1 :(得分:0)

这是显示此方法的sqlfiddle

select personid, 
         datepart(month, date) as Month, 
         datepart(year, date) as year, 
         case when [present] is not null then 1 else 0 end as present,
         case when [absence] is not null then 1 else 0 end as absence,
         case when [III] is not null then 1 else 0 end as III,
         case when [holiday] is not null then 1 else 0 end as holiday,
         sum(case when datediff(hour, gatein, gateout) <= 4 then 1 else 0 end) as LessThan4Hours,
         sum(case when datediff(hour, gatein, gateout) > 4 and datediff(hour, gatein, gateout) <= 5 then 1 else 0 end) as GreaterThan4Hours,
         sum(case when datediff(hour, gatein, gateout) > 5 then 1 else 0 end) as GreaterThan5Hours
  from 
  (
    select a.*, act.name
    from attendance a
    join activity act on a.activityid = act.activityid
  ) as source
  pivot
  (
    max(name)
    FOR name in ([present],[absence],[III],[holiday])
  ) as p
  group by personid, 
           datepart(month, date), 
           datepart(year, date), 
           case when [present] is not null then 1 else 0 end,
           case when [absence] is not null then 1 else 0 end,
           case when [III] is not null then 1 else 0 end,
           case when [holiday] is not null then 1 else 0 end