SQL 服务器 - 一天总时间
SQL Server - Sum Time pr Day
我需要一些关于 SQL 查询的建议。目前使用 SQL Server Express 2016。我有两个 table。 Master Table 称为 Dive,detail table 称为 DiveLog。它是一个用于记录石油和天然气行业水下设备事件的系统。
以下是 table 的详细信息:
CREATE TABLE [dbo].[Dives](
[DiveNo] [int] IDENTITY(1,1) NOT NULL,
[ROVID] [smallint] NULL,
[ClientID] [int] NULL,
[ProjectID] [int] NULL,
[WorksiteID] [int] NULL,
[TaskID] [int] NULL,
CONSTRAINT [PK_Dives] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[DiveLog](
[LP] [int] IDENTITY(1,1) NOT NULL,
[Time] [datetime] NULL,
[Activity] [smallint] NULL,
[Comment] [varchar](max) NULL,
[DiveNo] [int] NOT NULL,
CONSTRAINT [PK_DiveLog] PRIMARY KEY CLUSTERED
每次潜水都会创造一项新记录 table。在潜水期间,会记录各种潜水事件。例如:
Time Comment
2017-07-01 12:03:22 - Equipment in the water
2017-07-01 12:06:34 - Starting job
2017-07-01 15:03:55 - Job completed
2017-07-01 16:08:01 - Equipment on deck, dive complete
现在我需要创建给定时间段内每天潜水时间的报告。每天可以进行多次潜水,一次潜水可以跨越多天。我需要总结每天 00:00 到 24:00 的潜水时间。详细程度基于分钟
它应该看起来像这样:
Date Time
2017-07-01 - 23:33,
2017-07-02 - 24:00,
2017-07-03 - 01:00,
或者只能用分钟来概括,我会在我的app里转换成时间
Date Sum minutes
2017-07-01 - 435,
2017-07-02 - 109,
2017-07-03 - 597,
所以显然必须确定每次潜水的最大和最小日期,然后在午夜打破每 24 小时的时间段总结它
任何帮助都会很棒!
我假设 ACTIVITY 列类似于 'start'、'end'。如果不是,您将不得不找到另一种方法来可靠地识别哪个定时事件是开始,哪个是结束:
SELECT
start_times.diveno,
SUM(DATEDIFF(minute, start_times.starttime, end_times.endtime))
FROM
(
SELECT
diveno,
[time] as start_time,
ROW_NUMBER() OVER(PARTITION BY diveno ORDER BY [time] ASC) as start_counter
FROM
DiveLog
WHERE
activity = 'start_dive'
) start_times
INNER JOIN
(
SELECT
diveno,
[time] as end_time,
ROW_NUMBER() OVER(PARTITION BY diveno ORDER BY [time] ASC) as end_counter
FROM
DiveLog
WHERE
activity = 'end_dive'
) end_times
ON
start_times.diveno = end_times.diveno AND start_counter = end_counter
GROUP BY
start_times.diveno
此解决方案假定特定工作的 2 次潜水活动不重叠,但这可能无关紧要:如果记录的两次潜水活动重叠,并且 activity 1 开始于 00:02 并且在 00:12(十分钟)结束,activity 2 从 00:07 开始,在 00:09(2 分钟)结束,那么总时长是 12 分钟,此代码将配对activity 1 的开始和 activity 2 的结束就好像它是 activity 1 的结束一样,并将 activity 1 记录为 7 分钟。然后它还会将 activity 2 记录为类似逻辑的 5 分钟。总时间又是 12 分钟,但我基本担心这是混淆了开始日期和结束日期
真的,您的应用也应该使用专用 activity ID 记录活动;此 activity id 对于与特定 activity
相关的每个条目都是相同的编号
"artificial days" 方法看起来像这样(注意:需要大量示例数据才能正确编写它 - 这些都已经写过了,从来没有 运行;如果有任何错误,请通过注释2:限制报告日期范围的方式是为参数 @searchStartDate
提供值 - 日期时间应该是第一天的午夜,例如“2017-01-01 00:00:00”和 @plusDaysToSearch
在此之后要查看的天数,例如 2017-01-01 之后接下来的 90 天为 90 天):
WITH dates AS (
SELECT @searchStartDate as d
UNION ALL
SELECT d + 1.0 FROM dates WHERE d < @searchStartDate + @plusDaysToSearch --cant go over 100 here without changing the recursion limit
)
SELECT
dates.d,
SUM(
DATEDIFF(
minute,
CASE WHEN dive_times.start_date != dates.d THEN dates.d ELSE dive_times.start_time END,
CASE WHEN dive_times.start_date != dates.d THEN dates.d ELSE dive_times.start_time END
)
) as minutes_dived_today
FROM
dives
CROSS APPLY
dates
LEFT JOIN
(
SELECT start_times.diveno, start_time, start_date, end_time, end_date
FROM
(
SELECT
diveno,
[time] as start_time,
convert(date, [time]) as start_date,
ROW_NUMBER() OVER(PARTITION BY diveno ORDER BY [time] ASC) as start_counter
FROM
DiveLog
WHERE
activity = 'start_dive'
) start_times
INNER JOIN
(
SELECT
diveno,
[time] as end_time,
convert(date, [time]) as start_date,
ROW_NUMBER() OVER(PARTITION BY diveno ORDER BY [time] ASC) as end_counter
FROM
DiveLog
WHERE
activity = 'end_dive'
) end_times
ON
start_times.diveno = end_times.diveno AND
start_times.start_counter = end_times.end_counter
) dive_times
ON
dives.diveno = end_times.diveno AND
dates.d BETWEEN dive_times.start_date AND dive_times.end_date
GROUP BY
dates.d
我需要一些关于 SQL 查询的建议。目前使用 SQL Server Express 2016。我有两个 table。 Master Table 称为 Dive,detail table 称为 DiveLog。它是一个用于记录石油和天然气行业水下设备事件的系统。
以下是 table 的详细信息:
CREATE TABLE [dbo].[Dives](
[DiveNo] [int] IDENTITY(1,1) NOT NULL,
[ROVID] [smallint] NULL,
[ClientID] [int] NULL,
[ProjectID] [int] NULL,
[WorksiteID] [int] NULL,
[TaskID] [int] NULL,
CONSTRAINT [PK_Dives] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[DiveLog](
[LP] [int] IDENTITY(1,1) NOT NULL,
[Time] [datetime] NULL,
[Activity] [smallint] NULL,
[Comment] [varchar](max) NULL,
[DiveNo] [int] NOT NULL,
CONSTRAINT [PK_DiveLog] PRIMARY KEY CLUSTERED
每次潜水都会创造一项新记录 table。在潜水期间,会记录各种潜水事件。例如:
Time Comment
2017-07-01 12:03:22 - Equipment in the water
2017-07-01 12:06:34 - Starting job
2017-07-01 15:03:55 - Job completed
2017-07-01 16:08:01 - Equipment on deck, dive complete
现在我需要创建给定时间段内每天潜水时间的报告。每天可以进行多次潜水,一次潜水可以跨越多天。我需要总结每天 00:00 到 24:00 的潜水时间。详细程度基于分钟
它应该看起来像这样:
Date Time
2017-07-01 - 23:33,
2017-07-02 - 24:00,
2017-07-03 - 01:00,
或者只能用分钟来概括,我会在我的app里转换成时间
Date Sum minutes
2017-07-01 - 435,
2017-07-02 - 109,
2017-07-03 - 597,
所以显然必须确定每次潜水的最大和最小日期,然后在午夜打破每 24 小时的时间段总结它
任何帮助都会很棒!
我假设 ACTIVITY 列类似于 'start'、'end'。如果不是,您将不得不找到另一种方法来可靠地识别哪个定时事件是开始,哪个是结束:
SELECT
start_times.diveno,
SUM(DATEDIFF(minute, start_times.starttime, end_times.endtime))
FROM
(
SELECT
diveno,
[time] as start_time,
ROW_NUMBER() OVER(PARTITION BY diveno ORDER BY [time] ASC) as start_counter
FROM
DiveLog
WHERE
activity = 'start_dive'
) start_times
INNER JOIN
(
SELECT
diveno,
[time] as end_time,
ROW_NUMBER() OVER(PARTITION BY diveno ORDER BY [time] ASC) as end_counter
FROM
DiveLog
WHERE
activity = 'end_dive'
) end_times
ON
start_times.diveno = end_times.diveno AND start_counter = end_counter
GROUP BY
start_times.diveno
此解决方案假定特定工作的 2 次潜水活动不重叠,但这可能无关紧要:如果记录的两次潜水活动重叠,并且 activity 1 开始于 00:02 并且在 00:12(十分钟)结束,activity 2 从 00:07 开始,在 00:09(2 分钟)结束,那么总时长是 12 分钟,此代码将配对activity 1 的开始和 activity 2 的结束就好像它是 activity 1 的结束一样,并将 activity 1 记录为 7 分钟。然后它还会将 activity 2 记录为类似逻辑的 5 分钟。总时间又是 12 分钟,但我基本担心这是混淆了开始日期和结束日期
真的,您的应用也应该使用专用 activity ID 记录活动;此 activity id 对于与特定 activity
相关的每个条目都是相同的编号"artificial days" 方法看起来像这样(注意:需要大量示例数据才能正确编写它 - 这些都已经写过了,从来没有 运行;如果有任何错误,请通过注释2:限制报告日期范围的方式是为参数 @searchStartDate
提供值 - 日期时间应该是第一天的午夜,例如“2017-01-01 00:00:00”和 @plusDaysToSearch
在此之后要查看的天数,例如 2017-01-01 之后接下来的 90 天为 90 天):
WITH dates AS (
SELECT @searchStartDate as d
UNION ALL
SELECT d + 1.0 FROM dates WHERE d < @searchStartDate + @plusDaysToSearch --cant go over 100 here without changing the recursion limit
)
SELECT
dates.d,
SUM(
DATEDIFF(
minute,
CASE WHEN dive_times.start_date != dates.d THEN dates.d ELSE dive_times.start_time END,
CASE WHEN dive_times.start_date != dates.d THEN dates.d ELSE dive_times.start_time END
)
) as minutes_dived_today
FROM
dives
CROSS APPLY
dates
LEFT JOIN
(
SELECT start_times.diveno, start_time, start_date, end_time, end_date
FROM
(
SELECT
diveno,
[time] as start_time,
convert(date, [time]) as start_date,
ROW_NUMBER() OVER(PARTITION BY diveno ORDER BY [time] ASC) as start_counter
FROM
DiveLog
WHERE
activity = 'start_dive'
) start_times
INNER JOIN
(
SELECT
diveno,
[time] as end_time,
convert(date, [time]) as start_date,
ROW_NUMBER() OVER(PARTITION BY diveno ORDER BY [time] ASC) as end_counter
FROM
DiveLog
WHERE
activity = 'end_dive'
) end_times
ON
start_times.diveno = end_times.diveno AND
start_times.start_counter = end_times.end_counter
) dive_times
ON
dives.diveno = end_times.diveno AND
dates.d BETWEEN dive_times.start_date AND dive_times.end_date
GROUP BY
dates.d