我想只返回一小时的单一记录

时间:2014-07-16 10:27:47

标签: sql sql-server sql-server-2008

以下查询工作正常,但它返回两行小时,我不想

SELECT
    USERINFO.name, USERINFO.BADGENUMBER, 
    departments.deptname, APPROVEDHRS.hours,
    sum(workingdays) as workingdays,TotalWorkingDays
FROM
    (SELECT DISTINCT 
         (DATEDIFF(DAY, '2014-06-01', '2014-06-30') + 1) -
          DATEDIFF(WEEK, '2014-06-01', '2014-06-30') * 2 - 
          (CASE WHEN DATEPART(WEEKDAY, '2014-06-01') = 5 THEN 1 ELSE 0 END) - 
          (CASE WHEN DATEPART(WEEKDAY, '2014-06-30') = 6 THEN 1 ELSE 0 END) AS TotalWorkingDays, 
         COUNT(DISTINCT DATEADD(d, 0,DATEDIFF(d, 0, CHECKINOUT.CHECKTIME))) AS workingdays,
         USERINFO.BADGENUMBER, USERINFO.NAME, hours
     FROM  
         USERINFO 
     LEFT JOIN 
         CHECKINOUT ON USERINFO.USERID = CHECKINOUT.USERID 
     LEFT JOIN 
         departments ON departments.deptid = userinfo.DEFAULTDEPTID
         left join APPROVEDHRS on APPROVEDHRS.userid = userinfo.userid AND 
(APPROVEDHRS.DATE >='2014-06-01') AND (APPROVEDHRS.DATE <='2014-06-30')
     WHERE
         (DEPARTMENTS.DEPTNAME = 'xyz') 
         AND (CHECKINOUT.CHECKTIME >= '2014-06-01') 
         AND (CHECKINOUT.CHECKTIME <= '2014-06-30') 
     GROUP BY 
         hours, USERINFO.BADGENUMBER, deptname, USERINFO.NAME,
         CONVERT(VARCHAR(10), CHECKINOUT.CHECKTIME, 103)) blue
GROUP BY 
    name, BADGENUMBER, workingdays, TotalWorkingDays, deptname, hours

以上查询的输出:

name    BADGENUMBER     deptname        hours   
---------------------------------------------------
abc     1111             xyz            00:07:59    
abc     1111             xyz            00:08:00    
pqr     2222             qwe            NULL

现在表中的总小时数(APPROVEDHRS表)是:

BADGENUMBER     NAME    DATE        HOURS
-------------------------------------------------
1111            xyz  2014-06-15     00:07:59
1111            xyz  2014-06-14     00:08:00
1111            xyz  2014-07-14     00:10:00

我正在从2014-06-01到2014-06-30获取记录

所以我想要以下输出:

name       BADGENUMBER      deptname        hours   
--------------------------------------------------------       
    abc     1111             xyz            00:15:59    

    pqr     2222            qwe             NULL

帮助我获得所需的输出。

谢谢

3 个答案:

答案 0 :(得分:1)

显然,如果您想要将持续时间加在一起,则应将存储作为可以添加的内容。通常,这采用数字类型的形式,表示您感兴趣的最小粒度(在这种情况下显然为几分钟)。您可以将其包装为标准操作符所依据的实际定义类型(我确定有人为某些版本的SQL Server定义了INTERVAL类型),但实质上它是#s}简单地由INTEGER或其他东西支持。

如果您无法更改数据库中的实际类型,则需要将其转换为此语句(并返回显示)。通过基于此声明一对函数,这可能是最简单的:

CREATE FUNCTION dbo.Minutes_From_Duration_String (@Duration AS CHAR(8))
RETURNS INTEGER
BEGIN
    RETURN (CAST(SUBSTRING(@Duration, 1, 2) AS INTEGER) * 24 * 60) +
           (CAST(SUBSTRING(@Duration, 4, 2) AS INTEGER) * 60) +
           (CAST(SUBSTRING(@Duration, 7, 2) AS INTEGER))
END;

CREATE FUNCTION dbo.Duration_String_From_Minutes (@Minutes AS INTEGER)
RETURNS CHAR(10)
BEGIN
    RETURN RIGHT('00' + (@Minutes / 60 / 24), 2) + ':' +
           RIGHT('00' + ((@Minutes / 60) % 24), 2) + ':' +
           RIGHT('00' + (@Minutes % 60), 2)
END;

SQL Fiddle example

(请注意,这些非常是基本的,并会在最轻微的挑衅中爆炸。其余的留给读者练习。)

照顾它们,它们可以像往常一样在您的查询中使用。


请注意,我认为您的查询应该稍微修改一下。没有启动数据就有点难以辨别,但我相信它可以更快地运行,并且更清晰。

首先,对于SQL Server上列出的类型,始终将正连续范围类型(​​如日期/时间/时间戳)查询为lower-bound inclusive (>=), upper-bound exclusive (<)尤其是。这意味着你永远不必担心处理一些事情。

接下来,如果您还没有,那么您真的需要一个Calendar Table。在我看来,它是最有用的Dimension表。您可以根据需要添加基本上尽可能多的索引,这意味着您可以使用它们(和范围查询)来实际获得您之前无法完成的基于索引的聚合(即,按周等)。它还使得非工作日(假期)变得更加容易,并且对于另一件事情至关重要:DATEPART(WEEKDAY, ....)的结果依赖于当前会话的文化/区域设置。这可能不是你想要的。

如果你现在不能创建一个,你可以使用递归CTE轻松生成一个简单的:

WITH Calendar_Range AS (SELECT CAST('20140601' AS DATE) AS Calendar_Date,
                               dbo.ISO_Day_Of_Week(CAST('20140601' AS DATE)) AS Day_Of_Week
                        UNION ALL 
                        SELECT DATEADD(day, 1, Calendar_Date),
                               dbo.ISO_Day_Of_Week(DATEADD(day, 1, Calendar_Date))
                        FROM Calendar_Range
                        WHERE Calendar_Date < CAST('20140701' AS DATE))
SELECT Calendar_Date, Day_Of_Week 
FROM Calendar_Range

SQL Fiddle demo

(这假设您有一些方法可以获得ISO星期几 - 星期一是1.该演示包含一个样本函数来执行此操作。)

我们实际上有三种不同的聚合,所以我们需要将它们全部分开:
首先,批准的总小时数:

SELECT userid, SUM(dbo.Minutes_From_Duration_String(hours)) AS totalHours
FROM ApprovedHrs
WHERE date >= CAST('20140601' AS DATE) 
      AND date < CAST('20140701' AS DATE)
GROUP BY userid

然后,总工作天数。

SELECT CheckInOut.userid, COUNT(DISTINCT Calendar_Range.calendar_date) AS daysWorked
FROM Calendar_Range
JOIN CheckInOut
     ON CheckInOut.checkTime >= Calendar_Range.calendar_date
        AND CheckInOut.checkTime < DATEADD(day, 1, Calendar_Range.calendar_date)
WHERE Calendar_Range.calendar_date >= CAST('20140601' AS DATE) 
      AND Calendar_Range.calendar_date < CAST('20140701' AS DATE)
GROUP BY CheckInOut.userid

(我假设Calendar_Range是一个完整的日历表,包含所有可能的日期)

最后,天数&#34;可用&#34;工作:

SELECT COUNT(*) AS totalWorkingDays
FROM Calendar_Range
WHERE Day_Of_Week NOT IN (6, 7)
      AND calendar_date >= CAST('20140601' AS DATE) 
      AND calendar_date < CAST('20140701' AS DATE)

(我假设还有其他非工作日不应该算在这里,比如圣诞节,但我没有包含条件。否则,你可以做到计算方法与您之前的工作类似,只需注意一周中的问题。我在此处使用的查询假设ISO星期几值)

我们现在拥有了所需的所有部分,因此我们可以组装最终查询:

SELECT UserInfo.name, UserInfo.badgeNumber,
       Departments.deptName,
       dbo.Duration_String_From_Minutes(COALESCE(SummedHours.totalHours, 0)) AS totalHours,
       COALESCE(DaysWorked.daysWorked, 0) AS daysWorked,
       WorkingDays.totalWorkingDays
FROM UserInfo
JOIN Departments
  ON Departments.deptId = UserInfo.defaultDeptId
     AND Departments.deptName = 'xyz'
CROSS JOIN (SELECT COUNT(*) AS totalWorkingDays
            FROM Calendar_Range
            WHERE Day_Of_Week NOT IN (6, 7)
                  AND calendar_date >= CAST('20140601' AS DATE) 
                  AND calendar_date < CAST('20140701' AS DATE)) WorkingDays
LEFT JOIN (SELECT userid, SUM(dbo.Minutes_From_Duration_String(hours)) AS totalHours
           FROM ApprovedHrs
           WHERE date >= CAST('20140601' AS DATE) 
                 AND date < CAST('20140701' AS DATE)
           GROUP BY userid) SummedHours
       ON SummedHours.userId = UserInfo.userId
LEFT JOIN (SELECT CheckInOut.userid, COUNT(DISTINCT Calendar_Range.calendar_date) AS daysWorked
           FROM Calendar_Range
           JOIN CheckInOut
             ON CheckInOut.checkTime >= Calendar_Range.calendar_date
                AND CheckInOut.checkTime < DATEADD(day, 1, Calendar_Range.calendar_date)
           WHERE Calendar_Range.calendar_date >= CAST('20140601' AS DATE) 
                 AND Calendar_Range.calendar_date < CAST('20140701' AS DATE)
           GROUP BY CheckInOut.userid) DaysWorked
       ON DaysWorked.userId = UserInfo.userId

答案 1 :(得分:0)

试试这个。

WITH CTE (name,BADGENUMBER,deptname,hours)
  AS
  (
  YOUR FULL QUERY  
  )
  SELECT name,BADGENUMBER,deptname,SUM(hours)
  FROM CTE
  GROUP BY name,BADGENUMBER,deptname

答案 2 :(得分:0)

你可以通过将varchar转换为时间,然后转换为秒,将它们相加,返回到varchar,返回时间来计算小时数:)这应该是总和和所需的转换:

   CONVERT(VARCHAR, dateadd(s,SUM(DATEDIFF(SECOND, 0, CAST(hours AS TIME))),0),114)