我在使用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
非常感谢任何帮助
答案 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
我也包含了演示代码和演示表结构。这一点仅在startdate
和enddate
列上。您需要将其调整为所需的表结构。但是,如果没有逻辑,只需添加几列就不难了。 : - )
以下是演示输入:
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.