使用 SQL 服务器中的存储过程提取值
Extract values with stored procedure in SQL Server
我需要你的帮助来在 SQL 服务器 (v12.0.6024.0) 中创建一个视图。我的一位客户有一个 table,其中一些时间段以这种格式保存:
ID
ID_EVENT
Time Slot
1000
24
08:30:00.0000
1000
24
09:00:00.0000
1000
24
09:30:00.0000
每个时间段持续 30 分钟,上面的示例表示 ID 为 24 的事件(保存在另一个 table 中)持续形式为 8:30 到 10:00(第 3 个时间段开始于 9:30,持续了 30 分钟,所以在 10:00 结束。问题是在某些情况下时间值不连续,中间可能会有停顿,所以我会这样:
ID
ID_EVENT
Time Slot
1000
24
08:30:00.0000
1000
24
09:00:00.0000
1000
24
09:30:00.0000
1000
24
11:30:00.0000
1000
24
12:00:00.0000
1000
24
12:30:00.0000
在这种情况下,ID 为 24 的事件从 8:30 持续到 10,停止,然后从 11:30 到 13:00 再次开始。我被要求为外部开发人员准备一个视图,其中我不仅要报告事件开始的时间(在我的示例中,8:30),而且它永远停止的时间(在我的示例中,13:00) 以及暂停开始的时间(在我的示例中 10:00)和暂停结束的时间(在我的示例中 11:30)。
我对前 2 个值没有问题,但我不知道如何提取其他两个值。我认为我们可以考虑在 2 个时间段不连续时发生暂停,同一事件不能超过多个时间段。我想我需要一个过程,但发现很难写;我需要一个显示
的视图
ID
ID_EVENT
Time1
Time2
Time3
Time4
1000
24
08:30:00.0000
10:00:00.0000
11:30:00.0000
13:00:00.0000
有什么帮助吗?
declare @t table(ID int, ID_EVENT int, TimeSlot time)
insert into @t
values
(1000, 24, '08:30:00.0000'),
(1000, 24, '09:00:00.0000'),
(1000, 24, '09:30:00.0000'),
--
(1000, 24, '11:30:00.0000'),
(1000, 24, '12:00:00.0000'),
(1000, 24, '12:30:00.0000'),
--
(1000, 24, '15:00:00.0000'),
(1000, 24, '15:30:00.0000'),
(1000, 24, '16:00:00.0000'),
--
(1000, 25, '15:30:00.0000'),
(1000, 25, '16:30:00.0000');
select Id, ID_EVENT,
min(TimeSlot) as StartTimeSlot,
dateadd(minute, 30, max(TimeSlot)) as EndTimeSlot
from
(
select *,
datediff(minute, '00:00:00', Timeslot)/30 - row_number() over(partition by Id, ID_EVENT order by TimeSlot) as grpid
from @t
) as t
group by Id, ID_EVENT, grpid;
--first two groups per event&id row
select Id, ID_EVENT,
--1
min(case when grpordinal = 1 then TimeSlot end) as StartSlot1,
dateadd(minute, 30, max(case when grpordinal = 1 then TimeSlot end)) as EndSlot1,
--2
min(case when grpordinal = 2 then TimeSlot end) as StartSlot2,
dateadd(minute, 30, max(case when grpordinal = 2 then TimeSlot end)) as EndSlot2
from
(
select Id, ID_EVENT, TimeSlot,
dense_rank() over(partition by Id, ID_EVENT order by grpid) as grpordinal
from
(
select *,
datediff(minute, '00:00:00', Timeslot)/30 - row_number() over(partition by Id, ID_EVENT order by TimeSlot) as grpid
from @t
) as t
) as src
--where grpordinal <= 2 --not really needed
group by Id, ID_EVENT;
--!!!!only when there are max two groups/periods
--if there could be more than 2 periods this will not work
select Id, ID_EVENT,
--1
min(case when grpid = 0 then TimeSlot end) as StartSlot1,
dateadd(minute, 30, max(case when grpid = 0 then TimeSlot end)) as EndSlot1,
--2
min(case when grpid <> 0 then TimeSlot end) as StartSlot2,
dateadd(minute, 30, max(case when grpid <> 0 then TimeSlot end)) as EndSlot2
from
(
select *,
/*
1
+ datediff(minute, '00:00:00', Timeslot)/30 - row_number() over(partition by Id, ID_EVENT order by TimeSlot)
- datediff(minute, '00:00:00', min(Timeslot) over(partition by Id, ID_EVENT)) /30
*/
1
+ datediff(minute, min(Timeslot) over(partition by Id, ID_EVENT), TimeSlot)/30
- row_number() over(partition by Id, ID_EVENT order by TimeSlot)
as grpid --1st groupid is always 0
from @t
) as t
group by Id, ID_EVENT;
这看起来像是一个间隙和孤岛问题,您要在其中识别“相邻”时隙并将其组合在一起。
我建议将范围放在行中而不是列中。为此,您可以使用 window 这样的函数:
select id, id_event,
min(timeslot) as timeslot_start, max(timeslot) as timeslot_end
from (
select t.*,
row_number() over(partition by id, id_event order by timeslot) rn
from mytable t
) t
group by id, id_event, datediff(minute, - rn * 30, timeslot)
如果只想查看每个事件的前两个范围 - 都在结果集中的同一行 - 那么我们可以在该查询之上使用条件聚合:
select id, id_event,
max(case when rn = 1 then timeslot_start end) as timeslot_start_1,
max(case when rn = 1 then timeslot_end end) as timeslot_end_1,
max(case when rn = 2 then timeslot_start end) as timeslot_start_2,
max(case when rn = 2 then timeslot_end end) as timeslot_end_2
from (
select id, id_event,
min(timeslot) as timeslot_start, max(timeslot) as timeslot_end,
row_number() over(partition by id, id_event order by min(timeslot)) rn
from (
select t.*,
row_number() over(partition by id, id_event order by timeslot) rn
from mytable t
) t
group by id, id_event, datediff(minute, - rn * 30, timeslot)
) t
where rn <= 2
group by id, id_event
我需要你的帮助来在 SQL 服务器 (v12.0.6024.0) 中创建一个视图。我的一位客户有一个 table,其中一些时间段以这种格式保存:
ID | ID_EVENT | Time Slot |
---|---|---|
1000 | 24 | 08:30:00.0000 |
1000 | 24 | 09:00:00.0000 |
1000 | 24 | 09:30:00.0000 |
每个时间段持续 30 分钟,上面的示例表示 ID 为 24 的事件(保存在另一个 table 中)持续形式为 8:30 到 10:00(第 3 个时间段开始于 9:30,持续了 30 分钟,所以在 10:00 结束。问题是在某些情况下时间值不连续,中间可能会有停顿,所以我会这样:
ID | ID_EVENT | Time Slot |
---|---|---|
1000 | 24 | 08:30:00.0000 |
1000 | 24 | 09:00:00.0000 |
1000 | 24 | 09:30:00.0000 |
1000 | 24 | 11:30:00.0000 |
1000 | 24 | 12:00:00.0000 |
1000 | 24 | 12:30:00.0000 |
在这种情况下,ID 为 24 的事件从 8:30 持续到 10,停止,然后从 11:30 到 13:00 再次开始。我被要求为外部开发人员准备一个视图,其中我不仅要报告事件开始的时间(在我的示例中,8:30),而且它永远停止的时间(在我的示例中,13:00) 以及暂停开始的时间(在我的示例中 10:00)和暂停结束的时间(在我的示例中 11:30)。
我对前 2 个值没有问题,但我不知道如何提取其他两个值。我认为我们可以考虑在 2 个时间段不连续时发生暂停,同一事件不能超过多个时间段。我想我需要一个过程,但发现很难写;我需要一个显示
的视图ID | ID_EVENT | Time1 | Time2 | Time3 | Time4 |
---|---|---|---|---|---|
1000 | 24 | 08:30:00.0000 | 10:00:00.0000 | 11:30:00.0000 | 13:00:00.0000 |
有什么帮助吗?
declare @t table(ID int, ID_EVENT int, TimeSlot time)
insert into @t
values
(1000, 24, '08:30:00.0000'),
(1000, 24, '09:00:00.0000'),
(1000, 24, '09:30:00.0000'),
--
(1000, 24, '11:30:00.0000'),
(1000, 24, '12:00:00.0000'),
(1000, 24, '12:30:00.0000'),
--
(1000, 24, '15:00:00.0000'),
(1000, 24, '15:30:00.0000'),
(1000, 24, '16:00:00.0000'),
--
(1000, 25, '15:30:00.0000'),
(1000, 25, '16:30:00.0000');
select Id, ID_EVENT,
min(TimeSlot) as StartTimeSlot,
dateadd(minute, 30, max(TimeSlot)) as EndTimeSlot
from
(
select *,
datediff(minute, '00:00:00', Timeslot)/30 - row_number() over(partition by Id, ID_EVENT order by TimeSlot) as grpid
from @t
) as t
group by Id, ID_EVENT, grpid;
--first two groups per event&id row
select Id, ID_EVENT,
--1
min(case when grpordinal = 1 then TimeSlot end) as StartSlot1,
dateadd(minute, 30, max(case when grpordinal = 1 then TimeSlot end)) as EndSlot1,
--2
min(case when grpordinal = 2 then TimeSlot end) as StartSlot2,
dateadd(minute, 30, max(case when grpordinal = 2 then TimeSlot end)) as EndSlot2
from
(
select Id, ID_EVENT, TimeSlot,
dense_rank() over(partition by Id, ID_EVENT order by grpid) as grpordinal
from
(
select *,
datediff(minute, '00:00:00', Timeslot)/30 - row_number() over(partition by Id, ID_EVENT order by TimeSlot) as grpid
from @t
) as t
) as src
--where grpordinal <= 2 --not really needed
group by Id, ID_EVENT;
--!!!!only when there are max two groups/periods
--if there could be more than 2 periods this will not work
select Id, ID_EVENT,
--1
min(case when grpid = 0 then TimeSlot end) as StartSlot1,
dateadd(minute, 30, max(case when grpid = 0 then TimeSlot end)) as EndSlot1,
--2
min(case when grpid <> 0 then TimeSlot end) as StartSlot2,
dateadd(minute, 30, max(case when grpid <> 0 then TimeSlot end)) as EndSlot2
from
(
select *,
/*
1
+ datediff(minute, '00:00:00', Timeslot)/30 - row_number() over(partition by Id, ID_EVENT order by TimeSlot)
- datediff(minute, '00:00:00', min(Timeslot) over(partition by Id, ID_EVENT)) /30
*/
1
+ datediff(minute, min(Timeslot) over(partition by Id, ID_EVENT), TimeSlot)/30
- row_number() over(partition by Id, ID_EVENT order by TimeSlot)
as grpid --1st groupid is always 0
from @t
) as t
group by Id, ID_EVENT;
这看起来像是一个间隙和孤岛问题,您要在其中识别“相邻”时隙并将其组合在一起。
我建议将范围放在行中而不是列中。为此,您可以使用 window 这样的函数:
select id, id_event,
min(timeslot) as timeslot_start, max(timeslot) as timeslot_end
from (
select t.*,
row_number() over(partition by id, id_event order by timeslot) rn
from mytable t
) t
group by id, id_event, datediff(minute, - rn * 30, timeslot)
如果只想查看每个事件的前两个范围 - 都在结果集中的同一行 - 那么我们可以在该查询之上使用条件聚合:
select id, id_event,
max(case when rn = 1 then timeslot_start end) as timeslot_start_1,
max(case when rn = 1 then timeslot_end end) as timeslot_end_1,
max(case when rn = 2 then timeslot_start end) as timeslot_start_2,
max(case when rn = 2 then timeslot_end end) as timeslot_end_2
from (
select id, id_event,
min(timeslot) as timeslot_start, max(timeslot) as timeslot_end,
row_number() over(partition by id, id_event order by min(timeslot)) rn
from (
select t.*,
row_number() over(partition by id, id_event order by timeslot) rn
from mytable t
) t
group by id, id_event, datediff(minute, - rn * 30, timeslot)
) t
where rn <= 2
group by id, id_event