2016-12-05 25 views
3

我有以下數據之間:T-SQL - 分鐘每小時兩個日期時間

| tid | startdate  | enddate    | 
| 1 | 2016-12-26 12:30 | 2016-12-26 15:30  | 
| 2 | 2016-12-26 13:15 | 2016-12-26 15:15  | 

我想創建一個小時數,然後分鐘的日期時間落入小時內量的結果。

實施例的結果:

| tid | hour | minutes_in | 
| 1 | 12 | 30   | 
| 1 | 13 | 60   | 
| 1 | 14 | 60   | 
| 1 | 15 | 30   | 
| 2 | 13 | 45   | 
| 2 | 14 | 60   | 
| 2 | 15 | 15   | 

任何建議?

+0

如何分鐘隔離 – TheGameiswar

+0

我CURI - 爲什麼你需要這個結果?這只是一個更大的目標嗎?感覺這有可能是一個[XY問題](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)。 – 3N1GM4

+0

每個tid都是用戶登錄/註銷時間。我需要每小時登錄分鐘。因此,如果三名用戶在12:00至13:00之間登錄30分鐘,這將導致90分鐘的登錄時間在12小時內。 – phicon

回答

5

首先你需要一個數字表,從0讓你的時間 - 23,它可以很容易的飛行與表值構造函數創建:

SELECT N 
FROM (VALUES 
     (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12), 
     (13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23) 
    ) n (N); 

然後你就可以加入這個以自己的原始數據將行分成所需的數量。然後你只需要一個CASE表達式應用正確的邏輯來計算分:

WITH Numbers (Number) AS 
( SELECT N 
    FROM (VALUES 
      (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12), 
      (13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23) 
     ) n (N) 
), SampleData (tid, StartDate, EndDate) AS 
( SELECT tid, CONVERT(DATETIME2, StartDate), CONVERT(DATETIME2, EndDate) 
    FROM (VALUES 
      (1, '2016-12-26 12:30', '2016-12-26 15:30'), 
      (2, '2016-12-26 13:15', '2016-12-26 15:15') 
     ) d (tid, StartDate, EndDate) 
) 
SELECT d.tid, 
     [Hour] = n.Number, 
     Minutes_in = CASE 
         -- SPECIAL CASE: START HOUR = END HOUR 
         WHEN DATEPART(HOUR, d.StartDate) = DATEPART(HOUR, d.EndDate) 
          THEN DATEDIFF(MINUTE, d.StartDate, d.EndDate) 

         -- FULL HOURS IN BETWEEN START AND END 
         WHEN n.Number > DATEPART(HOUR, d.StartDate) 
          AND n.Number < DATEPART(HOUR, d.EndDate) THEN 60 

         -- START HOUR 
         WHEN n.Number = DATEPART(HOUR, d.StartDate) 
          THEN 60 - DATEPART(MINUTE, d.StartDate) 

         -- END HOUR 
         WHEN n.Number = DATEPART(HOUR, d.EndDate) 
          THEN DATEPART(MINUTE, d.EndDate) 
        END 
FROM SampleData d 
     INNER JOIN Numbers n 
      ON n.Number >= DATEPART(HOUR, d.StartDate) 
      AND n.Number <= DATEPART(HOUR, d.EndDate); 

附錄

如果你需要跨越數日,那麼你可以稍稍改變邏輯,產生一個更大的集數字覆蓋更多的時間差,然後,而不是加入當天的小時,加入的人數從一開始的日期時間到結束日期時間的時間差:

SELECT * 
FROM SampleData d 
     INNER JOIN Numbers n 
      ON n.Number <= DATEDIFF(HOUR, d.StartDate, d.EndDate) 

這意味着地方該範圍跨越幾天,然後沒有問題,小時只是不斷增加。例如

WITH Numbers (Number) AS 
( SELECT ROW_NUMBER() OVER(ORDER BY N1.N) - 1 
    FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N1(N) 
    CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N2 (N) 
    CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N3 (N) 
    CROSS JOIN (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N4 (N) 
), SampleData (tid, StartDate, EndDate) AS 
( SELECT tid, CONVERT(DATETIME2, StartDate), CONVERT(DATETIME2, EndDate) 
    FROM (VALUES 
      (1, '2016-12-26 12:30', '2016-12-26 15:30'), 
      (2, '2016-12-26 13:15', '2016-12-26 15:15'), 
      (3, '2016-12-26 13:15', '2016-12-27 15:15') 
     ) d (tid, StartDate, EndDate) 
) 
SELECT d.tid, 
     [Date] = CONVERT(DATE, d.StartDate), 
     [Hour] = CONVERT(TIME(0), DATEADD(HOUR, DATEPART(HOUR, d.StartDate) + n.Number, 0)), 
     Minutes_in = CASE 
         -- SPECIAL CASE: START HOUR = END HOUR 
         WHEN DATEPART(HOUR, d.StartDate) = DATEPART(HOUR, d.EndDate) 
          AND DATEDIFF(DAY, d.StartDate, d.EndDate) = 0 
          THEN DATEDIFF(MINUTE, d.StartDate, d.EndDate) 

         -- START HOUR 
         WHEN n.Number = 0 
          THEN 60 - DATEPART(MINUTE, d.StartDate) 

         -- END HOUR 
         WHEN n.Number = DATEDIFF(HOUR, d.StartDate, d.EndDate) 
          THEN DATEPART(MINUTE, d.EndDate) 

         -- FULL HOURS IN BETWEEN START AND END 
         ELSE 60 

        END 
FROM SampleData d 
     INNER JOIN Numbers n 
      ON n.Number <= DATEDIFF(HOUR, d.StartDate, d.EndDate) 
ORDER BY d.tid, n.Number; 
+0

很好地完成了,我打算在小時和60-x的情況下做類似的事情,但還沒有那麼快就到達那裏。有一個upvote。 :) – Reisclef

+0

這正是我所要求的 - ty。雖然現在我看到了結果,我意識到我也需要日期。作爲開始日期和結束日期並不總是在同一天 - 可能會跨越多天。我會盡力用你的意見來做。 – phicon

+1

@phicon當範圍穿過一天或多天時,我已經增加了一點關於如何解決這個問題。 – GarethD

0

方法-I

您可以使用UDF(另一種簡單的方法)實現這一

讓您構建提供的數據模式

CREATE TABLE #TAB (TID INT, STARTDATE DATETIME, ENDDATE DATETIME) 

INSERT INTO #TAB 
SELECT 1,'2016-12-26 12:30','2016-12-26 15:30' 
UNION ALL 
SELECT 2,'2016-12-26 13:15','2016-12-26 15:15' 

創建一個UDF到生成from和之間的值

ALTER FUNCTION [dbo].[FN_GENERATE] (@FROM_NBR INT, @TO_NBR INT) 
RETURNS 
@RESULT TABLE(HR INT) 
AS 
BEGIN 
;WITH CTE AS 
(
    SELECT @FROM_NBR AS FROM_NBR,@TO_NBR AS TO_NBR 
    UNION ALL 
    SELECT FROM_NBR+1 ,TO_NBR FROM CTE WHERE FROM_NBR<TO_NBR 


) 
INSERT INTO @RESULT 
SELECT FROM_NBR FROM CTE 

    RETURN 
END 

現在通過調用函數來查詢數據。

;WITH CTE AS (
SELECT TID,STARTDATE,ENDDATE,DATEPART(HH,STARTDATE) FROM_HR , DATEPART(HH,ENDDATE) TO_HR FROM #TAB T 
) 
SELECT C1.TID,F.HR, COALESCE(DATEPART(MINUTE,FRM_HR_MINUTS.STARTDATE),DATEPART(MINUTE,TO_HR_MINUTS.ENDDATE),60) 
FROM CTE C1 
CROSS APPLY 
(
    SELECT * FROM DBO.[FN_GENERATE] (C1.FROM_HR, C1.TO_HR) 
)AS F 
LEFT JOIN CTE FRM_HR_MINUTS ON C1.TID= FRM_HR_MINUTS.TID AND DATEPART(HH,FRM_HR_MINUTS.STARTDATE)= F.HR 
LEFT JOIN CTE TO_HR_MINUTS ON C1.TID= TO_HR_MINUTS.TID AND DATEPART(HH,TO_HR_MINUTS.ENDDATE)= F.HR 

編輯:

方法 - II

不使用UDF &使用MASTER.DBO.SPT_VALUES

;WITH CTE AS (

--PREPARING START HR, END HR, START_MIN, END_MIN FROM #TAB 
SELECT TID,STARTDATE,ENDDATE 
,DATEPART(HH,STARTDATE) FROM_HR 
, DATEPART(HH,ENDDATE) TO_HR 
, DATEPART(MINUTE, STARTDATE) AS STARTMIN 
, DATEPART(MINUTE, ENDDATE) ENDMIN 

FROM #TAB T 
) 

SELECT TID 
, NUMBER AS HRS 
--if Outer APply produce Null Display Minutes from CTE else 60 Mins 
, CASE ISNULL(OA.FRM_MINS, C1.STARTMIN) + ISNULL(TO_MINS,C1.ENDMIN) 
     WHEN 0 
      THEN 60 
     ELSE ISNULL(OA.FRM_MINS, C1.STARTMIN) + ISNULL(TO_MINS,C1.ENDMIN) 
     END AS MINS 
FROM CTE C1 
OUTER APPLY --JOINING NUMBERS BETWEEN FROM_HR & TO_HR using MASTER.DBO.SPT_VALUES 
(
    SELECT NUMBER 
    --IF FROM_HR matched NULL Else 0 
    , CASE C1.FROM_HR WHEN NUMBER THEN NULL ELSE 0 END AS FRM_MINS 

    --IF TO_HR matched NULL Else 0 
    ,CASE C1.TO_HR WHEN NUMBER THEN NULL ELSE 0 END AS TO_MINS  

    FROM MASTER.DBO.SPT_VALUES 
    WHERE [type]='P' AND number>0 AND number BETWEEN FROM_HR AND TO_HR 

)AS OA 
相關問題