SQL 生成表,显示定义的事件集之间的日期跨度

Posted

技术标签:

【中文标题】SQL 生成表,显示定义的事件集之间的日期跨度【英文标题】:SQL generating table that shows date span between a defined set of events 【发布时间】:2020-02-05 09:16:53 【问题描述】:

我正在寻找一种方法来提取一个表格,该表格显示一系列已定义事件之间的日期。我一直在考虑 Partition By 结合控制表来完成这项工作。

我有下表包含所有交易

除此之外,还有一个控制表,最终用户可以在其中定义他们想要计算的日期范围。看起来是这样的

现在的问题是是否有可能通过组合这两个表可以产生以下输入的平滑和快速的 SQL 语句。输出保存了每个员工和区域的 fromdate 和 todate 组合,使用控制表最终计算事件之间的 DATEDIFF,如下所示:

    CREATE TABLE [dbo].[Fact](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [PersonID] [nvarchar](255) NULL,
    [PersonName] [nvarchar](255) NULL,
    [Area] [nvarchar](255) NULL,
    [EventID] [nvarchar](255) NULL,
    [Date] [date] NULL
    )
GO

    CREATE TABLE [dbo].[ControlTable](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [EventSequenceID] [nvarchar](255) NULL,
    [EventSequenceName] [nvarchar](255) NULL,
    [FromEvent] [nvarchar](255) NULL,
    [ToEvent] [nvarchar](255) NULL
)
GO

INSERT [dbo].[ControlTable] ([EventSequenceID], [EventSequenceName], [FromEvent], [ToEvent]) VALUES (N'1', N'Event E-> H', N'Event E', N'Event H')
GO
INSERT [dbo].[ControlTable] ([EventSequenceID], [EventSequenceName], [FromEvent], [ToEvent]) VALUES (N'2', N'Event K to H', N'Event K', N'Event H')
GO
INSERT [dbo].[ControlTable] ([EventSequenceID], [EventSequenceName], [FromEvent], [ToEvent]) VALUES (N'3', N'Event E to Event T', N'Event E', N'Event T')
GO

INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'1', N'Peter', N'Area X', N'Event E', CAST(N'2000-01-01' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'1', N'Peter', N'Area X', N'Event B', CAST(N'2000-01-03' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'1', N'Peter', N'Area X', N'Event K', CAST(N'2000-01-06' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'1', N'Peter', N'Area X', N'Event A', CAST(N'2000-01-09' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'1', N'Peter', N'Area X', N'Event H', CAST(N'2000-01-12' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'2', N'Lis', N'Area X', N'Event E', CAST(N'2000-01-01' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'2', N'Lis', N'Area X', N'Event K', CAST(N'2000-01-05' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'2', N'Lis', N'Area X', N'Event C', CAST(N'2000-01-06' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'2', N'Lis', N'Area X', N'Event H', CAST(N'2000-01-11' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'2', N'Lis', N'Area X', N'Event T', CAST(N'2000-01-28' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'2', N'Lis', N'Area B', N'Event E', CAST(N'2000-03-10' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'2', N'Lis', N'Area B', N'Event G', CAST(N'2000-03-14' AS Date))
GO
INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'2', N'Lis', N'Area B', N'Event H', CAST(N'2000-03-20' AS Date))
GO

同一个人/区域内可能有多个事件,例如。添加以下行意味着彼得和区域 X 有两个“事件 H”

  INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'1', N'Peter', N'Area X', N'Event H', CAST(N'2000-01-24' AS Date))

因此,作为最终结果,输出可以包括 from/to 事件的排名,例如

【问题讨论】:

您使用的是哪个 dbms? 如果同一事件在给定的人/区域出现两次怎么办? 它是 SQL Server。我可以看到同一组事件实际上可能发生在同一个人、同一地区 【参考方案1】:

假设同一个事件从不会出现在同一个 person/area 元组中两次,您可以加入表并进行条件聚合:

select
    ct.eventSequenceId,
    ct.eventSequenceName,
    f.personID,
    f.personName,
    f.area,
    min(case when f.eventID = ct.fromEvent then f.date end) fromDate,
    min(case when f.eventID = ct.toEvent then f.date end) toDate,
    datediff(
        day, 
        min(case when f.eventID = ct.fromEvent then f.date end),
        min(case when f.eventID = ct.toEvent then f.date end)
    ) dateDifference
from fact f
inner join controltable ct 
    on f.eventID in (ct.fromEvent, ct.toEvent)
group by
    ct.eventSequenceId,
    ct.eventSequenceName,
    f.personID,
    f.personName,
    f.area

计数(*) > 1 按 f.personID、ct.eventSequenceId 排序

having 子句过滤掉部分匹配项(即找到一个事件但未找到另一个)。

Demo on DB Fiddle

事件序列 ID |事件序列名称 |个人ID |人名 |面积 |从日期 |到日期 |日期差异 :---------------- | :----------------- | :------- | :--------- | :----- | :--------- | :--------- | -------------: 1 |事件 E-> H | 1 |彼得 | X区 | 2000-01-01 | 2000-01-12 | 11 2 |事件 K 到 H | 1 |彼得 | X区 | 2000-01-06 | 2000-01-12 | 6 1 |事件 E-> H | 2 |李斯 | B区 | 2000-03-10 | 2000-03-20 | 10 1 |事件 E-> H | 2 |李斯 | X区 | 2000-01-01 | 2000-01-11 | 10 2 |事件 K 到 H | 2 |李斯 | X区 | 2000-01-05 | 2000-01-11 | 6 3 |事件 E 到事件 T | 2 |李斯 | X区 | 2000-01-01 | 2000-01-28 | 27

【讨论】:

同一个 Person/Area 可以有多个事件。如果我们添加 INSERT [dbo].[Fact] ([PersonID], [PersonName], [Area], [EventID], [Date]) VALUES (N'1', N'Peter', N'Area X', N'Event H', CAST(N'2000-01-24' AS Date)) GO 然后对于 Person/Area 有两个“Event H”。可以输出然后包括排名->我在上面改变了

以上是关于SQL 生成表,显示定义的事件集之间的日期跨度的主要内容,如果未能解决你的问题,请参考以下文章

sql 显示重复记录数

sql 在MySQL中的两个日期之间生成随机日期

传入数据集,生成对应表的sql脚本

数据分析SQL日期维度表生成(含节假日)

数据分析SQL日期维度表生成(含节假日)

怎么查看查询方法自动生成的SQL语句