在 SQL Server 中使用存储过程提取值 [关闭]

Posted

技术标签:

【中文标题】在 SQL Server 中使用存储过程提取值 [关闭]【英文标题】:Extract values with stored procedure in SQL Server [closed] 【发布时间】:2020-12-29 13:59:19 【问题描述】:

我需要您的帮助才能在 SQL Server (v12.0.6024.0) 中创建视图。我的一个客户有一个表格,其中一些时间段以这种格式保存:

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 的事件(保存在另一个表中)从 8:30 持续到 10:00(第三个时间段从 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

有什么帮助吗?

【问题讨论】:

到目前为止你尝试了什么? 如果有 2 个“暂停”会发生什么;你期待6个时间列吗?如果有 4 个,您是否期望 10 个时间列?如果是这样,那么您不能在 VIEW 中执行此操作。 另外,您的标题说“故事”过程(我假设您的意思是存储过程),但您问题中的所有内容都在谈论VIEWVIEWPROCEDURE非常不同的对象类型。你实际上追求什么? 嗨@Larnu,我必须准备一个视图;我想我需要编写一个过程来调用查询以提取 2 个缺失的字段。无论如何,我永远不会有 2 次暂停,每个过程都将包含一个或两个时间间隔。至于标题,我的意思显然是“Stored”,storied 是一个错字。 但是如果你想要一个存储过程,为什么你的问题中的所有内容都是关于 VIEW 的?就像我说的,VIEWPROCEDURE非常 不同的对象类型。你实际上追求什么? 【参考方案1】:
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;

【讨论】:

感谢您的帮助 @Iptr 但有问题,我得到 2 行而不是 ID_Event 25 的 1 行 ...@SCdev... 两行之间的差异(id_event=25)超过 30 分钟。因此这两行不连续,它们属于(两个)不同的组. 我的坏@lptr,你是对的,我没有注意到这一点。对不起! ..@SCdev ..没问题..从您的问题中这里缺少什么? --> there cannot be more than periods for the same event. ..只能有 2 个句点,而您希望它们(2 个句点)彼此相邻,作为 4 列,用于一个事件行? 是的@Iptr,我需要在一行中结束所有时间。如果事件仅包含一个间隔,则 2 列将为空或 NULL ,如果它有 2 个句点,我将在所有 4 列中都有值【参考方案2】:

这看起来像是一个间隙和孤岛问题,您希望在其中识别“相邻”时隙并将其组合在一起。

我建议将范围放在行中而不是列中。为此,您可以使用如下窗口函数:

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 Server 中使用存储过程提取值 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SQL Server 通过 ETL 存储过程提取数据

在 SQL Server 存储过程中拆分文本

SQL Server存储过程中使用表值作为输入参数示例

SQL Server:在存储过程中的变量中存储多个值

如何在sql server中创建一个存储过程中的varchar型的输入参数默认值为空

SQL Server:表值函数与存储过程