简体   繁体   English

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

[英]Calculating the duration between two dates in sql

I'm having some difficulty with an SQL query for reporting machine efficiency from a CNC monitoring system. 我在使用SQL查询来从CNC监视系统报告机器效率时遇到一些困难。 The monitoring software records the duration the machines are in different states like machining, stopped, out of work setup etc. I want to total up the duration of these states for each machine per day to determine their efficiency. 监控软件记录了机器处于不同状态(例如,加工,停止,无法正常工作等)的持续时间。我想每天汇总每台机器的这些状态的持续时间,以确定其效率。

Each state is given a start and end time I initially thought it would be a simple case of calculating the difference between the two dates but its not that simple. 我最初以为每个状态都指定了开始时间和结束时间,这是计算两个日期之间差异的简单情况,但并不是那么简单。 The problem is a state can start late one day and end in the early hours of the next which making it difficult to calculate state duration per day. 问题在于,状态可能会在一天的开始时间晚些时候开始,并在下一天的早些时候结束,这使得每天的状态持续时间难以计算。

Here's a link to an Excel file that contains sample data https://dl.dropboxusercontent.com/u/71259257/example%20dataset.xlsx 这是指向包含示例数据的Excel文件的链接https://dl.dropboxusercontent.com/u/71259257/example%20dataset.xlsx

I have come up with a fairly messy solution, I was going to use this as a basis to create a scheduled stored procedure that outputs the processed data to a separate table. 我想出了一个相当凌乱的解决方案,我将以此为基础来创建计划的存储过程,该过程将处理后的数据输出到单独的表中。

Here's my query 这是我的查询

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

What I want to know is does anyone have any better ideas? 我想知道的是,有人有更好的主意吗? Ideally I'd like to create view but currently I'm relying on an SQL variable to hold a date value that I was going to increment all dates in the table using a while loop. 理想情况下,我想创建视图,但是当前我依赖于一个SQL变量来保存一个日期值,该值将使用while循环来递增表中的所有日期。

Code for CalculateReportDuration function 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

Any help is greatly appreciated 任何帮助是极大的赞赏

You can simply use DATEDIFF() on your two dates. 您可以在两个日期上简单地使用DATEDIFF()

Here is a simple example: 这是一个简单的示例:

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

Read more here . 在这里阅读更多。

As the OP stated in the comments, he want's to have a row for each day. 正如OP在评论中指出的那样,他希望每天都有一行。 This means the time should be split per day and afterwards a calculation should be done. 这意味着应该每天分配时间,然后进行计算。

Here is the code to achieve this. 这是实现此目的的代码。 Just keep in Mind, due to the fact that it's done with a CTE you should be aware of statements which are over 100 days. 请注意,由于CTE已经完成,因此您应该注意超过100天的声明。 ;-) Otherwise you need to adjust MAX_RECURSION . ;-)否则,您需要调整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

I've included demo code and a demo table structure as well. 我还包括了演示代码和演示表结构。 The point is just on the startdate and the enddate columns. 问题的关键是只是在startdateenddate列。 You need to adjust it to your needed table structure. 您需要将其调整为所需的表结构。 But that won't be to hard to add just a few columns without logic. 但这并不难添加几行没有逻辑的列。 :-) :-)

Here is the demo input: 这是演示输入:

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

And this is the result of the query: 这是查询的结果:

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

Btw. 顺便说一句。 the ORDER BY in the result around the subquery isn't needed if you won't the result in an ordered state. 如果您不希望结果处于有序状态,则不需要在子查询周围的结果中使用ORDER BY

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. 没有对此进行测试,因此希望它能起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM