在这种情况下我遇到了解决Gaps和Islands类型的问题。 我想计算Microsoft SQL中的总停机时间。有没有我可以产生以下输出?谢谢!
实际停机时间=总停机时间 - 重叠时间
在这种情况下:
机器A:14小时
机器B:5小时但重叠4小时
机器C:1小时
机器D:2小时
机器E:重叠1小时
机器F:重叠2小时但1小时
实际停机时间总共为19小时
我的表格是一个查询。请告诉我如何查询。谢谢!
答案 0 :(得分:1)
这是基于Itzik Ben-Gan技术的解决方案(在下面的来源中注明)。该解决方案使用DENSE_RANK函数。代码已完成 - 可以将其复制到SSMS查询窗口并执行。
USE tempdb
GO
IF OBJECT_ID('dbo.GetNums', 'IF') IS NOT NULL
DROP FUNCTION dbo.GetNums;
GO
/* dbo.GetNums function is from Itzik Ben-Gan's article on packing intervals:
(http://blogs.solidq.com/en/sqlserver/packing-intervals/). */
CREATE FUNCTION dbo.GetNums(@n AS BIGINT)
RETURNS TABLE
AS
RETURN
WITH
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM L5)
SELECT TOP (@n) n FROM Nums ORDER BY n;
GO
IF OBJECT_ID('dbo.Production', 'U') IS NOT NULL
DROP TABLE dbo.Production;
GO
CREATE TABLE dbo.Production
(
production_line INT NOT NULL,
machine CHAR(1) NOT NULL,
[date] DATE NOT NULL,
time_started TIME NOT NULL,
time_completed TIME NOT NULL,
CONSTRAINT PK_Production PRIMARY KEY(production_line, machine)
);
INSERT INTO dbo.Production
(production_line, machine, [date], time_started, time_completed)
VALUES
(1, 'A', '2018-01-16', '00:00:00', '14:00:00'),
(1, 'B', '2018-01-16', '10:00:00', '15:00:00'),
(1, 'C', '2018-01-16', '17:00:00', '18:00:00'),
(1, 'D', '2018-01-16', '21:00:00', '23:00:00'),
(1, 'E', '2018-01-16', '21:30:00', '22:30:00'),
(1, 'F', '2018-01-16', '17:00:00', '19:00:00');
/* Algorithm adapted from "Microsoft SQL Server 2012
High-Performance T-SQL Using Window Functions" by
Itzik Ben-Gan (p. 198). */
DECLARE @production_date AS DATE = '2018-01-16';
DECLARE @from AS TIME = '00:00:00';
DECLARE @to AS TIME = '23:59:59';
WITH Hours AS
(
SELECT
DATEADD(hour, (nums.n - 1), @from) AS hr
FROM
dbo.GetNums(24 /* Hours in a day. */) AS nums
),
Groups AS
(
SELECT
H.hr,
DATEADD(hour, -1 * DENSE_RANK() OVER (ORDER BY H.hr), H.hr) AS grp
FROM
dbo.Production AS P
INNER JOIN Hours AS H ON H.hr BETWEEN P.time_started AND P.time_completed
WHERE
p.[date] = @production_date
),
Ranges AS
(
SELECT
MIN(hr) AS range_start,
MAX(hr) AS range_end
FROM
Groups
GROUP BY
grp
)
SELECT
SUM(DATEDIFF(hour, range_start, range_end)) AS hours_of_downtime
FROM
Ranges
DROP FUNCTION dbo.GetNums;
DROP TABLE dbo.Production;
编辑:回答OP关于他们的数据是否来自查询的问题。此修改示例删除临时dbo.Production
表,并添加Production
公用表表达式。
USE tempdb
GO
IF OBJECT_ID('dbo.GetNums', 'IF') IS NOT NULL
DROP FUNCTION dbo.GetNums;
GO
/* dbo.GetNums function is from Itzik Ben-Gan's article on packing intervals:
(http://blogs.solidq.com/en/sqlserver/packing-intervals/). */
CREATE FUNCTION dbo.GetNums(@n AS BIGINT)
RETURNS TABLE
AS
RETURN
WITH
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B),
L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B),
L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B),
L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B),
L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM L5)
SELECT TOP (@n) n FROM Nums ORDER BY n;
GO
/* Algorithm adapted from "Microsoft SQL Server 2012
High-Performance T-SQL Using Window Functions" by
Itzik Ben-Gan (p. 198). */
DECLARE @production_date AS DATE = '2018-01-16';
DECLARE @from AS TIME = '00:00:00';
DECLARE @to AS TIME = '23:59:59';
WITH Hours AS
(
SELECT
DATEADD(hour, (nums.n - 1), @from) AS hr
FROM
dbo.GetNums(24 /* Hours in a day. */) AS nums
),
Production AS
(
SELECT
production_line,
machine,
[date],
time_started,
time_completed
FROM
production_table
WHERE
[date] = @production_date
),
Groups AS
(
SELECT
H.hr,
DATEADD(hour, -1 * DENSE_RANK() OVER (ORDER BY H.hr), H.hr) AS grp
FROM
Production AS P
INNER JOIN Hours AS H ON H.hr BETWEEN P.time_started AND P.time_completed
),
Ranges AS
(
SELECT
MIN(hr) AS range_start,
MAX(hr) AS range_end
FROM
Groups
GROUP BY
grp
)
SELECT
SUM(DATEDIFF(hour, range_start, range_end)) AS hours_of_downtime
FROM
Ranges
DROP FUNCTION dbo.GetNums;
答案 1 :(得分:0)
这非常难看,但这就是我所做的:
我使用上面的示例数据创建了一个表格并得到了答案:19。
代码是:
WITH aset
AS (
SELECT [Machine]
, [Date]
, [TimeStarted]
, TimeCompleted
FROM [CEA_DBA].[dbo].[LineInteruptions]
WHERE date = '2018-01-16'),
overlaps
AS (
SELECT a.machine
AS a_machine
, b.machine
AS b_machine
, CASE
WHEN a.TimeStarted <= b.TimeStarted
THEN a.TimeStarted
ELSE b.TimeStarted
END
AS timeStarted
, CASE
WHEN a.TimeCompleted >= b.TimeCompleted
THEN a.TimeCompleted
ELSE b.TimeCompleted
END
AS timeCompleted
FROM aset
AS a
CROSS JOIN aset
AS b
WHERE b.TimeStarted <= a.timeCompleted
AND b.timecompleted >= a.timecompleted
AND a.Machine <> b.Machine),
nonoverlaps
AS (
SELECT aset.timeStarted
, aset.timeCompleted
FROM aset
LEFT OUTER JOIN overlaps
AS oa ON aset.Machine = oa.a_machine
LEFT OUTER JOIN overlaps
AS ob ON aset.Machine = ob.b_machine
WHERE oa.a_machine IS NULL
AND ob.b_machine IS NULL),
gset
AS (
SELECT TimeStarted
, TimeCompleted
FROM overlaps
UNION ALL
SELECT timestarted
, timecompleted
FROM nonoverlaps)
SELECT SUM(DATEDIFF(hour, TimeStarted, timeCompleted))
AS downtime
FROM gset;