2014-12-04 67 views
1

,我有以下數據庫中的數據:如何在兩個日期時間內獲取2個日期時間並獲得每個日期的小時數?

Job   ClockInDateTime  ClockOutDateTime 
MM00151509 2013-11-19 07:01 2013-11-19 09:20 
MM00151800 2013-11-09 09:08 2013-11-20 11:36 
MM00153591 2013-12-01 08:20 2013-12-03 08:15 
MM00154121 2013-12-05 08:19 2013-12-05 10:32 

我想借此每一行,並顯示與該日期的相關小時沿的日期。正如您在上面的結果中看到的,許多記錄跨越ClockInDateTime和ClockOutDateTime之間的天數。數千個工作(行)。

什麼是SQL查詢來做到這一點?

例輸出一個工作上面 - MM00153591:

Job   ClockInDateTime  ClockOutDateTime Date  Hours 
MM00153591 2013-12-01 08:20 2013-12-03 08:26 12/1/2013 15.3333 
MM00153591 2013-12-01 08:20 2013-12-03 08:26 12/2/2013 24 
MM00153591 2013-12-01 08:20 2013-12-03 08:15 12/3/2013 8.25 
+0

semi-dupe:http://stackoverflow.com/questions/14119277/subtract-two-dates-in-sql-and-get-days-of-the-result – 2014-12-04 21:52:32

+1

這不是真正的重複,因爲上面的一個不顯示如何獲得小時和日期。 – Kevin 2014-12-04 21:55:20

回答

1

這裏的技巧是要想得到導致1行指定的日期之間每天連續爆炸。

這可以通過幾種方式來實現,但一種方法是加入一個簡單的日期表,其中包含所有可能的日期(在合理的範圍內),其中連接標準在範圍內。根據您的性能需求,可以實時構建或預先構建並存儲以備後用。

下面是一個例子,假設你的源3列是一個名爲作業表:

DECLARE @MinDate DATETIME 
DECLARE @TotalDays INT 

SELECT @MinDate = DATEADD(dd,-1, CONVERT(DATE,MIN(ClockInDateTime))), 
     @TotalDays = DATEDIFF(dd, CONVERT(DATE,MIN(ClockInDateTime)), CONVERT(DATE,MAX(ClockOutDateTime))) + 2 
    FROM Job 

SELECT J.Job, J.ClockInDateTime, J.ClockOutDateTime, 
    CONVERT(DATE, Dt) as [Date], 
    CASE WHEN ClockInDateTime < Dt AND ClockOutDateTime >= DATEADD(dd, 1, Dt) THEN 24*60 
     WHEN ClockInDateTime >= Dt AND ClockOutDateTime >= DATEADD(dd, 1, Dt) 
      THEN DATEDIFF(mi, ClockInDateTime, DATEADD(dd, 1, Dt)) 
     WHEN ClockInDateTime < Dt AND ClockOutDateTime < DATEADD(dd, 1, Dt) 
      THEN DATEDIFF(mi, Dt, ClockOutDateTime) 
     ELSE DATEDIFF(mi, ClockInDateTime, ClockOutDateTime) 
    END/60.0 as Hours 
    FROM Job J 
    INNER JOIN (SELECT TOP (@TotalDays) DATEADD(dd, ROW_NUMBER() OVER (ORDER BY s1.object_id), @MinDate) as Dt 
        FROM sys.objects s1 
        CROSS JOIN sys.objects s2) as DateTable 
     ON Dt BETWEEN CONVERT(DATE,J.ClockInDateTime) AND CONVERT(DATE,J.ClockOutDateTime) 
    ORDER BY Job, [Date] 

與樣本數據的一些簡單的測試得到什麼似乎是正確的結果,但對於工作,你叫了我並得到略微不同的答案第一天的總時數:

Job   ClockInDateTime  ClockOutDateTime Date  Hours 
MM00153591 2013-12-01 08:20 2013-12-03 08:26 12/1/2013 15.6666 
MM00153591 2013-12-01 08:20 2013-12-03 08:26 12/2/2013 24 
MM00153591 2013-12-01 08:20 2013-12-03 08:15 12/3/2013 8.25 

注:我使用上面建立的日期表的方法是產生一個「數字表」一個簡單的方法,但也有其他的,也許更可靠的方法:What is the best way to create and populate a numbers table?

編輯:這裏是對所提供的示例數據的完整腳本,以及不使用日期表的替代方法,而是在整個集合中的每一天循環。提供了每種方法的全部結果不變也:

SET NOCOUNT ON 

IF OBJECT_ID('tempdb..#Table') IS NOT NULL DROP TABLE #Table 
CREATE TABLE #Table (Job NVARCHAR(256), ClockInDateTime DATETIME, ClockOutDateTime DATETIME) 

INSERT INTO #Table (Job, ClockInDateTime, ClockOutDateTime) 
      SELECT N'MM00151509', '2013-11-19 07:01', '2013-11-19 09:20' 
UNION ALL SELECT N'MM00151800','2013-11-09 09:08','2013-11-20 11:36' 
UNION ALL SELECT N'MM00153591','2013-12-01 08:20','2013-12-03 08:15' 
UNION ALL SELECT N'MM00154121','2013-12-05 08:19','2013-12-05 10:32' 

PRINT 'Method 1: Calculate as a subset of all possible days' 
DECLARE @MinDate DATETIME 
DECLARE @TotalDays INT 

SELECT @MinDate = DATEADD(dd,-1, CONVERT(DATE,MIN(ClockInDateTime))), 
     @TotalDays = DATEDIFF(dd, CONVERT(DATE,MIN(ClockInDateTime)), CONVERT(DATE,MAX(ClockOutDateTime))) + 2 
    FROM #Table 

SELECT J.Job, J.ClockInDateTime, J.ClockOutDateTime, 
    CONVERT(DATE, Dt) as [Date], 
    CASE WHEN ClockInDateTime < Dt AND ClockOutDateTime >= DATEADD(dd, 1, Dt) THEN 24*60 
     WHEN ClockInDateTime >= Dt AND ClockOutDateTime >= DATEADD(dd, 1, Dt) 
      THEN DATEDIFF(mi, ClockInDateTime, DATEADD(dd, 1, Dt)) 
     WHEN ClockInDateTime < Dt AND ClockOutDateTime < DATEADD(dd, 1, Dt) 
      THEN DATEDIFF(mi, Dt, ClockOutDateTime) 
     ELSE DATEDIFF(mi, ClockInDateTime, ClockOutDateTime) 
    END/60.0 as Hours 
    FROM #Table J 
    INNER JOIN (SELECT TOP (@TotalDays) DATEADD(dd, ROW_NUMBER() OVER (ORDER BY s1.object_id), @MinDate) as Dt 
        FROM sys.objects s1 
        CROSS JOIN sys.objects s2) as DateTable 
     ON Dt BETWEEN CONVERT(DATE,J.ClockInDateTime) AND CONVERT(DATE,J.ClockOutDateTime) 
    ORDER BY Job, [Date] 


PRINT 'Method 2: Loop 1 day at a time' 
GO 
IF OBJECT_ID('dbo.udf_MinDate') IS NOT NULL DROP FUNCTION dbo.udf_MinDate 
GO 
IF OBJECT_ID('dbo.udf_MaxDate') IS NOT NULL DROP FUNCTION dbo.udf_MaxDate 
GO 
CREATE FUNCTION dbo.udf_MinDate(@Date1 DATETIME, @Date2 DATETIME) 
RETURNS DATETIME 
AS 
BEGIN 
    RETURN CASE WHEN @Date1 < @Date2 THEN @Date1 ELSE @Date2 END 
END 
GO 
CREATE FUNCTION dbo.udf_MaxDate(@Date1 DATETIME, @Date2 DATETIME) 
RETURNS DATETIME 
AS 
BEGIN 
    RETURN CASE WHEN @Date1 > @Date2 THEN @Date1 ELSE @Date2 END 
END 
GO 
IF OBJECT_ID('tempdb..#TempResult') IS NOT NULL 
    DROP TABLE #TempResult 

CREATE TABLE #TempResult (Job NVARCHAR(256), ClockInDateTime DATETIME, ClockOutDateTime DATETIME, [Date] DATE, [Hours] DECIMAL(18,6)) 

DECLARE @MaxDaysDifferent INT, @CurDayOffset INT 

SELECT @MaxDaysDifferent = MAX(DATEDIFF(dd, ClockInDateTime, ClockOutDateTime)) 
    FROM #Table 

SET @CurDayoffset = 0 
WHILE (@CurDayOffset <= @MaxDaysDifferent) 
BEGIN 
    INSERT INTO #TempResult (Job, ClockInDateTime, ClockOutDateTime, [Date], Hours) 
     SELECT T.Job, T.ClockInDateTime, T.ClockOutDateTime, 
       DATEADD(dd, @CurDayOffset, CONVERT(DATE,T.ClockInDateTime)), 
       DATEDIFF(mi, dbo.udf_MaxDate(T.ClockInDateTime, DATEADD(dd, @CurDayOffset, CONVERT(DATE,T.ClockInDateTime))), 
          dbo.udf_MinDate(T.ClockOutDateTime, DATEADD(dd, @CurDayOffset + 1, CONVERT(DATE,T.ClockInDateTime))))/60.0 as [Hours] 
      FROM #Table T 
      WHERE DATEADD(dd, @CurDayOffset, CONVERT(DATE,T.ClockInDateTime)) <= T.ClockOutDateTime 

    SET @CurDayOffset = @CurDayOffset + 1 
END 

SELECT * FROM #TempResult 
    ORDER BY Job, [Date] 

此查詢的全部結果是:

Method 1: Calculate as a subset of all possible days 
Job       ClockInDateTime   ClockOutDateTime  Date  Hours 
------------------------------ ----------------------- ----------------------- ---------- ------------------------------ 
MM00151509      2013-11-19 07:01:00.000 2013-11-19 09:20:00.000 2013-11-19 2.316666 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-09 14.866666 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-10 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-11 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-12 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-13 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-14 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-15 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-16 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-17 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-18 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-19 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-20 11.600000 
MM00153591      2013-12-01 08:20:00.000 2013-12-03 08:15:00.000 2013-12-01 15.666666 
MM00153591      2013-12-01 08:20:00.000 2013-12-03 08:15:00.000 2013-12-02 24.000000 
MM00153591      2013-12-01 08:20:00.000 2013-12-03 08:15:00.000 2013-12-03 8.250000 
MM00154121      2013-12-05 08:19:00.000 2013-12-05 10:32:00.000 2013-12-05 2.216666 

Method 2: Loop 1 day at a time 
Job       ClockInDateTime   ClockOutDateTime  Date  Hours 
------------------------------ ----------------------- ----------------------- ---------- ------------------------------ 
MM00151509      2013-11-19 07:01:00.000 2013-11-19 09:20:00.000 2013-11-19 2.316666 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-09 14.866666 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-10 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-11 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-12 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-13 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-14 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-15 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-16 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-17 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-18 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-19 24.000000 
MM00151800      2013-11-09 09:08:00.000 2013-11-20 11:36:00.000 2013-11-20 11.600000 
MM00153591      2013-12-01 08:20:00.000 2013-12-03 08:15:00.000 2013-12-01 15.666666 
MM00153591      2013-12-01 08:20:00.000 2013-12-03 08:15:00.000 2013-12-02 24.000000 
MM00153591      2013-12-01 08:20:00.000 2013-12-03 08:15:00.000 2013-12-03 8.250000 
MM00154121      2013-12-05 08:19:00.000 2013-12-05 10:32:00.000 2013-12-05 2.216666 

注意的是,在第二個方法我介紹了一些UDF的只是爲了讓小時計算出現一點清潔劑。另外請注意,兩種方法產生相同的結果(這次我在Sql Server 2012 express上運行示例,但我沒有使用任何2012特定功能)。

最後,關於您最初的預期結果與這些小時結果之間的差異。我相信你的預期結果是不正確的。 08:20和午夜之間的時間爲15小時40分,即15.6667小時,而不是15.333小時。

+0

有沒有辦法做到這一點,而不使用日期表?怎麼樣? – Kevin 2014-12-05 14:31:29

+0

我不相信這會奏效。我已經厭倦了很多次才能使它起作用,並且我沒有得到正確的數據。 – Kevin 2014-12-05 22:08:16

+1

你能描述你在查詢時遇到的問題嗎?在Sql Server 2008 R2上的一個小測試集上使用上面的示例數據,我看到了你所要求的東西。或者我在請求中遺漏了一些東西。出於好奇,sys.objects表中有多少個對象? – lheria 2014-12-06 02:17:57

相關問題