日移计算

时间:2014-09-08 05:17:03

标签: sql-server datetime sql-server-2012

输入表

Start time     End Time
8/12/14 17:00  8/14/14 12:00

我需要将输出显示如下

Date     11:00 to 23:00    23:00 to 11:00
8/12/14        6                1
8/13/14        12               12
8/14/14        1                11

我知道你们都会说“你们尝试了什么?”

但答案是“我想不出从哪里开始”

3 个答案:

答案 0 :(得分:1)

使用递归CTE:

SQL Fiddle

MS SQL Server 2012架构设置

查询1

DECLARE @StartDate DateTime = '2014-08-12 17:00'
DECLARE @EndDate DateTime = '2014-08-14 12:00'


;WITH CTE
AS
(
    SELECT @StartDate AS [Date], 
           CAST(@StartDate As time) AS [Time]

    UNION ALL

    SELECT DATEADD(HH, 1, [Date]) As [Date], 
           CAST(DATEADD(HH, 1, [Date]) AS Time) AS [Time]
    FROM CTE
    WHERE DATEADD(HH, 1, [Date]) < @EndDate
)
SELECT CAST([Date] AS Date) [Date], 
        SUM(CASE WHEN [Time] BETWEEN '11:00' AND '22:59' 
                THEN 1 ELSE 0 END) as [11:00 to 23:00],
        SUM(CASE WHEN [Time] BETWEEN '00:00' AND '10:59' 
              THEN 1
                WHEN  [Time] Between '23:00' and '23:59' THEN 1
       ELSE 0 END) as [23:00 to 11:00]
FROM CTE
GROUP BY CAST([Date] AS Date) 
ORDER BY [Date]

<强> Results

|       DATE | 11:00 TO 23:00 | 23:00 TO 11:00 |
|------------|----------------|----------------|
| 2014-08-12 |              6 |              1 |
| 2014-08-13 |             12 |             12 |
| 2014-08-14 |              1 |             11 |

答案 1 :(得分:1)

以下显示了使用Tally表(又名“数字表”表)的一种方法。

我做的主要假设:

  • 开始和结束时间都是已知的
  • 开始和结束时间是针对精确的小时数(例如,1:00,2:00,而不是13:23或00:00.00.007)。 (这就是我使用smalldatetime数据类型的原因)
  • 结束时间始终大于开始时间

我进一步假设数据存储在多个实体的表中 - 也就是说,您最终希望在一个查询中处理多个项目。如果你只想一次为一个项目做这件事,(a)下面的查询可以很容易地删除,(b)用C#或者任何调用语言可能更容易。

设置测试数据:

--  DROP TABLE Testing
CREATE TABLE Testing
 (
   EntryId    int  not null
  ,StartTime  smalldatetime  not null
  ,EndTime    smalldatetime  not null
 )

INSERT Testing values
  (1, 'Aug 12, 2014 17:00', 'Aug 14, 2014  12:00')  --  Original problem
 ,(2, 'Aug 11, 2014 00:00', 'Aug 11, 2014  23:00')  --  23 hours
 ,(3, 'Aug 11, 2014 00:00', 'Aug 12, 2014  00:00')  --  24 hour shift
 ,(4, 'Aug 11, 2014 12:00', 'Aug 12, 2014  12:00')  --  Noon to Noon

 ,(11, 'Aug 22, 2014 4:00',  'Aug 22, 2014  5:00')  --  One-hour problem cases
 ,(12, 'Aug 22, 2014 10:00', 'Aug 22, 2014  11:00') --  One-hour problem cases
 ,(13, 'Aug 22, 2014 11:00', 'Aug 22, 2014  12:00') --  One-hour problem cases
 ,(14, 'Aug 22, 2014 12:00', 'Aug 22, 2014  13:00') --  One-hour problem cases

 ,(21, 'Aug 23, 2014 18:00', 'Aug 23, 2014  19:00') --  One-hour problem cases
 ,(22, 'Aug 23, 2014 22:00', 'Aug 23, 2014  23:00') --  One-hour problem cases
 ,(23, 'Aug 23, 2014 23:00', 'Aug 24, 2014  00:00') --  One-hour problem cases
 ,(24, 'Aug 24, 2014 00:00', 'Aug 24, 2014   1:00') --  One-hour problem cases

我的日常工作:

DECLARE
  @Earliest smalldatetime
 ,@Latest   smalldatetime


--  This could be thrown in as a first CTE, but doing so would make the overall query that much less comprehensible.
SELECT
   @Earliest = min(StartTime)
  ,@Latest   = max(EndTime)
 from Testing
 --where <filtering criteria, if you're not parsing the whole table)

;WITH
  Pass0 as (select 1 as C union all select 1), --2 rows
  Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows
  Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows
  Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows
  Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows
  Tally as (select row_number() over(order by C) as Number from Pass4),
  DateRange as (select
                   dateadd(hh, ta.Number, @Earliest) ShiftHour
                 from Tally ta
                 where dateadd(hh, ta.Number, @Earliest) <= @Latest)
SELECT
   te.EntryId
  ,cast(dateadd(hh, -1, dr.Shifthour) as date)  [Date]
  ,sum(case when datepart(hh, dateadd(hh, -1, dr.Shifthour)) between 11 and 22 then 1 else 0 end)  [11:00 to 23:00]
  ,sum(case when datepart(hh, dateadd(hh, -1, dr.Shifthour)) between 11 and 22 then 0 else 1 end)  [23:00 to 11:00]
 from Testing te
  inner join DateRange dr
   on dr.ShiftHour > te.StartTime
    and dr.ShiftHour <= te.Endtime
 group by
   te.EntryId
  ,cast(dateadd(hh, -1, dr.Shifthour) as date)
 order by 
   te.EntryId
  ,cast(dateadd(hh, -1, dr.Shifthour) as date)

将其插入以显示没有分组的结果,这对于弄清楚那里发生了什么是非常宝贵的:

;WITH
  Pass0 as (select 1 as C union all select 1), --2 rows
  Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows
  Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows
  Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows
  Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows
  Tally as (select row_number() over(order by C) as Number from Pass4),
  DateRange as (select
                   dateadd(hh, ta.Number, @Earliest) ShiftHour
                 from Tally ta
                 where dateadd(hh, ta.Number, @Earliest) <= @Latest)
SELECT
    te.EntryId
   ,dateadd(hh, -1, dr.Shifthour)
   ,cast(dateadd(hh, -1, dr.Shifthour) as date)  [Date]
   ,datepart(hh, dateadd(hh, -1, dr.Shifthour))
  from Testing te
   inner join DateRange dr
    on dr.ShiftHour > te.StartTime
     and dr.ShiftHour <= te.Endtime
  order by 
    te.EntryId
   ,cast(dateadd(hh, -1, dr.Shifthour) as date)
   ,dateadd(hh, -1, dr.Shifthour)

困难的部分是:

  • 将第24个小时(8月23日00:00)送到前一天(8月24日)。这就是为什么我有一天会把所有东西都改回来[编辑]一个小时
  • 处理多个条目时
  • <>逻辑
  • 与“中午至中午”样本的结果看起来很傻的事实相协调。

是的,这可能是过度工程,但有时你只需要试一试。我不能说它在大型数据集上的表现如何,如果你的第一个和最后一个日期超过四年,你需要在构建计数表时添加“Pass5”。

答案 2 :(得分:0)

DECLARE @StartTime DATETIME='08/01/2013 00:00:00'
    ,@EndTime DATETIME='08/03/2013 23:00:00'
DECLARE @StartDate DATE,
    @Starthour INT,
    @Startmin INT,
    @EndDate DATE,
    @Endhour INT,
    @Endmin INT

SELECT @StartDate=CONVERT(DATE,@StartTime,101),@Starthour=DATEPART(HOUR,@StartTime),@Startmin=DATEPART(MINUTE,@StartTime)
SELECT @EndDate=CONVERT(DATE,@EndTime,101),@Endhour=DATEPART(HOUR,@EndTime),@Endmin=DATEPART(MINUTE,@EndTime)

DECLARE @T TABLE (StartTime DATETIME, EndTime DATETIME)
INSERT @T VALUES (DATEADD(MINUTE,-@Startmin,@StartTime), DATEADD(MINUTE,-@Endmin,@EndTime))

DECLARE @Final1 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)
DECLARE @Final2 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)
DECLARE @Final3 TABLE (ShiftDate DATETIME,[11:00 to 23:00] INT,[23:00 to 11:00] INT)

;WITH CTE AS
(
    SELECT StartTime AS dt, EndTime,StartTime
    FROM @T
    UNION  ALL
    SELECT DATEADD(MINUTE, 60, dt) AS dt, EndTime,DATEADD(MINUTE, 60,StartTime) AS StartTime
    FROM CTE
    WHERE dt < DATEADD(HOUR, -1, EndTime)
)
INSERT INTO @Final1([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT 
    CASE 
      WHEN CAST(dt AS TIME) >= '11:00' AND CAST(dt AS TIME) < '23:00' THEN 1 
      ELSE 0 
    END AS '11:00 to 23:00',
    CASE 
      WHEN CAST(dt AS TIME) >= '11:00' AND CAST(dt AS TIME) < '23:00' THEN 0 
      ELSE 1 
    END AS '23:00 to 11:00'
    ,StartTime
FROM CTE


INSERT INTO @Final2([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT SUM([11:00 to 23:00])*60 AS [11:00 to 23:00]
    ,SUM([23:00 to 11:00])*60 AS [23:00 to 11:00]
    ,CONVERT(DATE,DATEADD(MINUTE,-660,ShiftDate)) AS ShiftDate
FROM @Final1
GROUP BY CONVERT(DATE,DATEADD(MINUTE,-660,ShiftDate))


INSERT INTO @Final3([11:00 to 23:00],[23:00 to 11:00],ShiftDate)
SELECT CASE WHEN ShiftDate=@StartDate AND @Starthour BETWEEN 11 AND 22 THEN [11:00 to 23:00]-@Startmin ELSE [11:00 to 23:00] END AS [11:00 to 23:00],
        CASE WHEN ShiftDate=@StartDate AND @Starthour BETWEEN 23 AND 10 THEN [23:00 to 11:00]-@Startmin ELSE [23:00 to 11:00] END AS [23:00 to 11:00],
        ShiftDate
FROM @Final2


SELECT ShiftDate,
    CONVERT(VARCHAR(10),
                (CASE WHEN ShiftDate=@EndDate AND @Endhour BETWEEN 11 AND 22 
                    THEN [11:00 to 23:00]+@Endmin 
                ELSE [11:00 to 23:00] END)) AS [11:00 to 23:00],
    CONVERT(VARCHAR(10),
            (CASE WHEN ShiftDate=@EndDate AND @Endhour BETWEEN 23 AND 10 
                THEN [23:00 to 11:00]+@Endmin 
            ELSE [23:00 to 11:00] END)) AS [23:00 to 11:00]
FROM @Final3