计算sql中两个日期之间的持续时间

时间:2015-06-20 09:16:26

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

我在使用SQL查询时遇到一些困难,无法通过CNC监控系统报告机器效率。监控软件记录机器处于不同状态的持续时间,如加工,停止,失效设置等。我希望每天为每台机器总计这些状态的持续时间,以确定它们的效率。

每个州都有一个开始和结束时间我最初认为这将是计算两个日期之间差异的简单案例,但并非如此简单。问题是一个州可以在一天晚些时候开始,在下一天的早些时候结束,这使得很难计算每天的州持续时间。

这是指向包含示例数据的Excel文件的链接 https://dl.dropboxusercontent.com/u/71259257/example%20dataset.xlsx

我提出了一个相当混乱的解决方案,我将使用它作为创建计划存储过程的基础,该过程将处理后的数据输出到单独的表中。

这是我的查询

DECLARE @date DATE;
SET @Date= '16 June 2015';

SELECT 
    Name, State, 
    CASE 
        WHEN LEFT(CONVERT(VARCHAR, StartDate, 120), 10) = @Date 
             AND LEFT(CONVERT(VARCHAR, EndDate, 120), 10) = @Date 
          THEN dbo.CalculateReportDuration(StartDate, EndDate) 
    END as Duration
FROM 
    [emc].[dbo].[TMB_MachineStateReport]
WHERE 
    CONVERT(Date, StartDate) = @Date 
    AND CONVERT(Date, EndDate) = @Date 

UNION ALL

SELECT 
    Name, State,
    dbo.CalculateReportDuration( LEFT(CONVERT(VARCHAR, EndDate, 120), 10) + ' 00:00:00.0000000 +00:00', EndDate)  as Duration
FROM 
    [emc].[dbo].[TMB_MachineStateReport]
WHERE 
    CONVERT(Date, EndDate) = @Date 
    AND CONVERT(Date, StartDate) < @Date 

UNION ALL

SELECT 
    Name, State,
    dbo.CalculateReportDuration(StartDate, LEFT(CONVERT(VARCHAR, StartDate, 120), 10) + ' 23:59:59.9999999 +00:00')
FROM 
    [emc].[dbo].[TMB_MachineStateReport]
WHERE 
    CONVERT(Date, EndDate) > @Date 
    AND CONVERT(Date, StartDate) = @Date

我想知道的是,有没有人有更好的想法?理想情况下,我想创建视图,但目前我依靠一个SQL变量来保存一个日期值,我将使用while循环增加表中的所有日期。

CalculateReportDuration函数的代码

USE [emc]
GO
/****** Object:  UserDefinedFunction [dbo].[CalculateReportDuration]    Script Date: 06/20/2015 10:23:21 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[CalculateReportDuration]
            (
                @start DATETIMEOFFSET(7),
                @end DATETIMEOFFSET(7)
            )
            RETURNS INT
            AS
            BEGIN
                RETURN CAST(ROUND(DATEDIFF(SECOND, @start, @end) / 60.0, 0) AS INT) 
            END

非常感谢任何帮助

2 个答案:

答案 0 :(得分:0)

您可以在两个日期使用DATEDIFF()

这是一个简单的例子:

SELECT DATEDIFF(minutes, GETDATE(), DATEADD(hours,2,GETDATE()))

了解更多here

正如OP在评论中所述,他希望每天都有一排。这意味着时间应该每天分开,之后应该进行计算。

以下是实现此目的的代码。请记住,由于它是用CTE完成的,你应该知道超过100天的陈述。 ;-)否则你需要调整MAX_RECURSION

IF(OBJECT_ID(N'tempdb..#timing') IS NOT NULL )
    DROP TABLE #timing

-- Create demo data
CREATE TABLE #timing(id int identity(1,1), startdate datetime, enddate datetime)

INSERT INTO #timing(startdate, enddate)
VALUES (DATEADD(day,-1,GETDATE()),DATEADD(MINUTE,10,GETDATE())), -- time spanning 1 day + 10 min
        (DATEADD(MINUTE,-70,GETDATE()),GETDATE()), -- spanning the current date just over 70 minutes
        (DATEADD(day,-3,GETDATE()),GETDATE()) -- spanning 3 days

        SELECT t.startdate, t.enddate, CONVERT(date,t.startdate), CONVERT(date,t.enddate) FROM #timing as t

;WITH cte AS(
    SELECT id, t.startdate as orig_startdate, t.enddate as orig_enddate,
        t.startdate, 
        DATEADD(SECOND,-1,CONVERT(datetime,DATEADD(day,1,CONVERT(date,t.startdate)))) as enddate,
        1 as iteration
    FROM #timing as t
    WHERE CONVERT(date, t.startdate) <> CONVERT(date, t.enddate)
    UNION ALL
    SELECT c.id, c.orig_startdate, c.orig_enddate, 
        CONVERT(datetime,DATEADD(day,c.iteration,CONVERT(date,c.orig_startdate))) as startdate,
        CASE WHEN CONVERT(date,DATEADD(day,c.iteration,CONVERT(date,c.orig_startdate)))
                = CONVERT(date, c.orig_enddate)
             THEN c.orig_enddate
             ELSE DATEADD(SECOND,-1,CONVERT(datetime,DATEADD(day,c.iteration+1,CONVERT(date,c.orig_startdate))))
        END as enddate,
        c.iteration+1 as iteration
    FROM cte as c
    WHERE DATEADD(day,c.iteration,CONVERT(date,c.orig_startdate)) <= CONVERT(date, c.orig_enddate)
) 
SELECT data.id, data.startdate, data.enddate, data.currentDate, data.minutesCurrentDay
FROM (
    SELECT c.id, c.orig_startdate as startdate, c.orig_enddate as enddate, 
        CONVERT(date,c.startdate) as currentDate, 
        DATEDIFF(minute,c.startdate,c.enddate)+1 as minutesCurrentDay
        --1 due to the timeshifting to get the days in line
    FROM cte as c
    UNION ALL
    SELECT t.id, t.startdate, t.enddate, CONVERT(date, t.startdate) as currentDate, 
        DATEDIFF(minute,t.startdate, t.enddate) as minutesCurrentDay
    FROM #timing as t
    WHERE CONVERT(date,t.startdate) = CONVERT(date,t.enddate)
) as data
ORDER BY data.id, data.currentDate

-- Cleanup
DROP TABLE #timing

我也包含了演示代码和演示表结构。这一点仅在startdateenddate列上。您需要将其调整为所需的表结构。但是,如果没有逻辑,只需添加几列就不难了。 : - )

以下是演示输入:

id          startdate               enddate
----------- ----------------------- -----------------------
1           2015-06-19 12:55:32.313 2015-06-20 13:05:32.313
2           2015-06-20 11:45:32.313 2015-06-20 12:55:32.313
3           2015-06-17 12:55:32.313 2015-06-20 12:55:32.313

这是查询的结果:

id          startdate               enddate                 currentDate minutesCurrentDay
----------- ----------------------- ----------------------- ----------- -----------------
1           2015-06-19 13:03:35.887 2015-06-20 13:13:35.887 2015-06-19  657
1           2015-06-19 13:03:35.887 2015-06-20 13:13:35.887 2015-06-20  794
2           2015-06-20 11:53:35.887 2015-06-20 13:03:35.887 2015-06-20  70
3           2015-06-17 13:03:35.887 2015-06-20 13:03:35.887 2015-06-17  657
3           2015-06-17 13:03:35.887 2015-06-20 13:03:35.887 2015-06-18  1440
3           2015-06-17 13:03:35.887 2015-06-20 13:03:35.887 2015-06-19  1440
3           2015-06-17 13:03:35.887 2015-06-20 13:03:35.887 2015-06-20  784

顺便说一下。如果您不将结果置于有序状态,则不需要子查询周围结果中的ORDER BY

答案 1 :(得分:0)

So something like this?

declare @start date, @end date
set @start = '20150616'
set @end = dateadd(day, 1, @start)

SELECT 
    Name, State,
    datediff(second,
      case when StartDate < @start then @start else StartDate end,
      case when EndDate > @end then @end else EndDate end) / 60.0 as Duration
FROM 
    [emc].[dbo].[TMB_MachineStateReport]
WHERE 
    EndDate > @start 
    StartDate < @end

This takes everything where the range overlaps with the given day and calculates the time either from the start / end of the row or start / end of the day, depending on which one is on the day of question.

Didn't test this so hopefully it works.