这是一个基于 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;