如何进行时间登记?
How to take on time-registration?
我一直在寻找 StackExchange 的网站来讨论基本的想法,因为我几乎可以肯定我在一段时间前遇到过它,但我找不到任何测试版以外的网站,而且没有开发人员朋友深入讨论这个问题,让我来到 Whosebug 寻求帮助,所以这是我的问题。
事件
由于这些天我无事可做,所以我一直在研究 Toggle 和 Clockify 等应用程序,我想知道这些...
作为一个基本概念,用户可以为任何间隙注册 types
个事件,例如:
2017-12-28 00:00:00 ~ 2018-01-14 23:59:59 // vacation
2018-01-20 00:00:00 ~ 2018-01-20 23:59:59 // devOps event
2018-01-24 00:00:00 ~ 2018-01-24 12:00:00 // haircut
详细信息
如果我想要有关 1 月份事件的详细信息,我想得出的实际结论是
details = {
events: [{...}, {...}, {...}],
daysUsed: {
vacation: 10, // holidays+weekends not counted
internal: 1,
personal: 0,5
}
}
我遇到的纸上问题
即使我 "saved" 数据是原样,例如在 RDS table 中是
// evtId | user | from | to | eventType | description
1 | 1 | 2017-12-28 00:00:00 | 2018-01-14 23:59:59 | V | vacation
2 | 1 | 2018-01-20 00:00:00 | 2018-01-20 23:59:59 | I | devOps event
3 | 1 | 2018-01-24 00:00:00 | 2018-01-24 12:00:00 | P | haircut
我无法使用日期时间来 select 从 1 月 1 日到 1 月 31 日的范围
WHERE userId = 1 and from >= '...' and to <= '...'
我需要将每个事件分散成 dailyEvent 方式,以便实际计算与天数相关的任何事情,例如
// eventId | date | evtType | dayType (holiday | weekend | vacation)
1 | 2017-12-28 | V | vacation
1 | 2017-12-29 | V | vacation
1 | 2017-12-30 | V | vacation & weekend
1 | 2017-12-31 | V | vacation & weekend
1 | 2018-01-01 | V | vacation & holiday
1 | 2018-01-02 | V | vacation
1 | 2018-01-03 | V | vacation
1 | 2018-01-04 | V | vacation
1 | 2018-01-05 | V | vacation
...
有了这些数据,我就可以轻松地仅使用数据库进行一些计算,至少在应用程序中的日期和处理时间之间是这样
我查看了 MomentJs 库,看看我是否可以,例如,添加原始注册并在某种程度上获得我想要的结果,但它仅用于操作和计算日期,而不是日期范围......日历库会更有意义......没有找到任何 javascript 我可以看到他们解决问题的方法......
两个 table 而不是一个
我还考虑过在 (dailyEvent) 中设置一个辅助 table 时间间隔并每天用一个条目填充 table,这样可以更轻松地从 2 个日期检索数据...
问题是,当用户 edits/deletes 主要事件时,还有一个 table 需要维护...而且同步功能从来没有这么好用过 :/
- 为此类任务获取 hold/retrieve 数据的最佳方式是什么?
- 传播到日常活动中是最好的主意吗?它会以指数方式增长 table 不是吗?但是没有什么像设计良好的索引 table 不会关心这些,也许所有超过 5 年的事件都可以 "archived" 进入特殊的 table...
你怎么看?
已添加
为了让问题更简单,这就是我要深入研究的问题
如何查询得到:
events 1, 2, 4 when query day 3
events 1, 2, 3 when query day 5
events 1, 2, 3 when query from days 4 to 7
events 1, 2 when query from days 10 to 14
这里有一个测试 ms-sql 可以玩:
Hostname 3b5a01df-b75c-41f3-9489-a8ad01604961.sqlserver.sequelizer.com
Database db3b5a01dfb75c41f39489a8ad01604961
Username czhsclbcolgeznij
Password WrXF2Eozi2qWHAeYejFqRn8cPfQqZyh3FS2JteUHPZHnmoDhwxgVaeMJrVYUX6HR
在 pseudo-sql 中,要查找时间段的交集,您可以使用
select
*
from events
where startdate<=[end of day 3]
and enddate>=[start of day 3]
好吧,任务无法在 SQL 中完全解决(从期望的输出来看,我猜你已经意识到了这一点)。另外,我不会给出确切的解决方案,因为它可能过于宽泛,但如果有任何问题,我很乐意提供帮助。我查看了您的帐户,发现您在 C# 中有金色徽章,所以如果有任何建议,我会参考这种语言。
首先,你想要的对象很复杂,所以让我假设这样的模型:
public class Summary
{
public List<string> events { get; set; }
public Dictionary<char, decimal> daysUsed { get; set; }
= new Dictionary<char, decimal> { { 'I', 0 }, { 'V', 0 }, { 'P', 0 } };
}
我认为您关于存储事件的第一个建议更好,我选择了这种方法。
DDL
declare @table table (evtId int, userId int, startDate datetime, endDate datetime, eventType char(1), [description] varchar(1000))
insert into @table values
(1 , 1 , '2017-12-28 00:00:00' , '2018-01-14 23:59:59' , 'V' , 'vacation'),
(2 , 1 , '2018-01-20 00:00:00' , '2018-01-20 23:59:59' , 'I' , 'devOps event'),
(3 , 1 , '2018-01-24 00:00:00' , '2018-01-24 12:00:00' , 'P' , 'haircut')
现在,让我们来看看你想做什么:
首先,声明您想要摘要的期间 - 它可以在 C# 中并通过一些 SQL 方法或一些 ORM 传递到 DB。为简单起见,我使用了 SQL:
declare @startDate datetime, @endDate datetime
select @startDate = '2018-01-01 00:00:00.000', @endDate = '2018-01-31 23:59:59.999'
哪个是你的例子(1 月 1 日至 31 日期间)。
现在让我们尝试获取我们需要的信息:
首先,最重要的部分是定义CTE,它将执行任务:
获取在给定时间段(一月)内开始或结束的所有事件。注意:部分在给定时间段内的事件也将包括在内。
我们不需要给定时间段以外的任何数据,因此在 CTE 中,我们将用 1 月 1 日代替开始日期,即事件在给定时间段之前开始的时间。此外,我们将用结束日期代替,当活动在给定时间段后结束时,即 1 月 31 日
CTE:
;with cte as (
select evtId,
userId,
case when startDate < @startDate then @startDate else startDate end [startDate],
case when endDate > @endDate then @endDate else endDate end [endDate],
eventType,
[Description]
from @table
where (startDate between @startDate and @endDate) or
(endDate between @startDate and @endDate)
)
现在,我们从您的 table 中获得了“干净”的数据。查询时间:
对于第一部分,即 events: [{...}, {...}, {...}]
,我想下面的查询就足够了:
select [description] from cte
此查询的结果可以存储在 Summary
class 的 events
属性 中。
现在,我们需要填充 daysUsed
属性:
select eventType,
round(1.0 * sum(datediff(minute, startDate, endDate)) / (60 * 24), 2) [daysUsed]
from cte
group by eventType
以上查询将 return 以下数据:
eventType|daysUsed
I |1
P |0.5
V |14
一般来说,上述查询的结果总是:
eventType|daysUsed
I |value
P |value
V |value
这个结果可以很容易地用于填充 daysUsed
Summary
class 中的属性。
最后,您可以将 Summary
class 的对象序列化为 JSON。然后你就会得到想要的输出。
备注:
C# class 只是一个示例,可以帮助我形象化地了解每一步我想做什么。不知道你用的是什么技术
我知道,第二次查询(填充 daysUsed
)的结果与您的略有不同,因为我没有排除周末。我没有排除周末和节假日,因为这不是主要问题,我认为您可以轻松处理。
我用的是CTE,只能查询一次,所以严格来说,应该是一些临时的table或者CTE的内部查询可以作为其他查询中的子查询(在我看来这将是最简单的)。
如果我对问题的理解正确,您只是希望 return 在特定时期内活跃的事件。如果那是正确的,那么这是一个相对容易解决的问题。如果满足以下条件,我们可以认为事件在一段时间内处于活动状态:
事件的起始边缘或结束边缘包含在周期内或
事件跨越整个周期,在这种情况下,事件的开始边缘将小于周期的开始时间,事件的结束边缘将大于周期的结束时间。
以下示例演示:
-- -------------------------------------------------------
-- Creat sample table and data
-- -------------------------------------------------------
CREATE TABLE [dbo].[EventData](
[ID] [int] IDENTITY(1,1) NOT NULL,
[EventStart] [datetime] NOT NULL,
[EventEnd] [datetime] NOT NULL,
CONSTRAINT [PK_EventData] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[EventData] ON
GO
INSERT [dbo].[EventData] ([ID], [EventStart], [EventEnd]) VALUES (1, CAST(N'2018-01-01T00:00:00.000' AS DateTime), CAST(N'2018-01-13T00:00:00.000' AS DateTime))
GO
INSERT [dbo].[EventData] ([ID], [EventStart], [EventEnd]) VALUES (2, CAST(N'2018-01-03T00:00:00.000' AS DateTime), CAST(N'2018-01-15T00:00:00.000' AS DateTime))
GO
INSERT [dbo].[EventData] ([ID], [EventStart], [EventEnd]) VALUES (3, CAST(N'2018-01-04T00:00:00.000' AS DateTime), CAST(N'2018-01-06T00:00:00.000' AS DateTime))
GO
INSERT [dbo].[EventData] ([ID], [EventStart], [EventEnd]) VALUES (4, CAST(N'2018-01-02T00:00:00.000' AS DateTime), CAST(N'2018-01-04T00:00:00.000' AS DateTime))
GO
SET IDENTITY_INSERT [dbo].[EventData] OFF
GO
-- -------------------------------------------------------
-- Sample Query
-- -------------------------------------------------------
DECLARE @periodStart DATETIME = '10 Jan 2018'; -- Selection period start date and time
DECLARE @periodEnd DATETIME = '14 Jan 2018'; -- Selection period end date and time
SELECT *
FROM [EventData] [E]
WHERE (
( -- Check if event start time is contained within period
(@periodStart <= [E].[EventStart]) AND (@periodEnd > [E].[EventStart])
)
OR
(
-- Check if event end time is contained within period
(@periodStart < [E].[EventEnd]) AND (@periodEnd > [E].[EventEnd])
)
OR
(
-- Check if the event spans the entire period
(@periodStart >= [E].[EventStart]) AND (@periodEnd <= [E].[EventEnd])
)
)
我一直在寻找 StackExchange 的网站来讨论基本的想法,因为我几乎可以肯定我在一段时间前遇到过它,但我找不到任何测试版以外的网站,而且没有开发人员朋友深入讨论这个问题,让我来到 Whosebug 寻求帮助,所以这是我的问题。
事件
由于这些天我无事可做,所以我一直在研究 Toggle 和 Clockify 等应用程序,我想知道这些...
作为一个基本概念,用户可以为任何间隙注册 types
个事件,例如:
2017-12-28 00:00:00 ~ 2018-01-14 23:59:59 // vacation
2018-01-20 00:00:00 ~ 2018-01-20 23:59:59 // devOps event
2018-01-24 00:00:00 ~ 2018-01-24 12:00:00 // haircut
详细信息
如果我想要有关 1 月份事件的详细信息,我想得出的实际结论是
details = {
events: [{...}, {...}, {...}],
daysUsed: {
vacation: 10, // holidays+weekends not counted
internal: 1,
personal: 0,5
}
}
我遇到的纸上问题
即使我 "saved" 数据是原样,例如在 RDS table 中是
// evtId | user | from | to | eventType | description
1 | 1 | 2017-12-28 00:00:00 | 2018-01-14 23:59:59 | V | vacation
2 | 1 | 2018-01-20 00:00:00 | 2018-01-20 23:59:59 | I | devOps event
3 | 1 | 2018-01-24 00:00:00 | 2018-01-24 12:00:00 | P | haircut
我无法使用日期时间来 select 从 1 月 1 日到 1 月 31 日的范围
WHERE userId = 1 and from >= '...' and to <= '...'
我需要将每个事件分散成 dailyEvent 方式,以便实际计算与天数相关的任何事情,例如
// eventId | date | evtType | dayType (holiday | weekend | vacation)
1 | 2017-12-28 | V | vacation
1 | 2017-12-29 | V | vacation
1 | 2017-12-30 | V | vacation & weekend
1 | 2017-12-31 | V | vacation & weekend
1 | 2018-01-01 | V | vacation & holiday
1 | 2018-01-02 | V | vacation
1 | 2018-01-03 | V | vacation
1 | 2018-01-04 | V | vacation
1 | 2018-01-05 | V | vacation
...
有了这些数据,我就可以轻松地仅使用数据库进行一些计算,至少在应用程序中的日期和处理时间之间是这样
我查看了 MomentJs 库,看看我是否可以,例如,添加原始注册并在某种程度上获得我想要的结果,但它仅用于操作和计算日期,而不是日期范围......日历库会更有意义......没有找到任何 javascript 我可以看到他们解决问题的方法......
两个 table 而不是一个
我还考虑过在 (dailyEvent) 中设置一个辅助 table 时间间隔并每天用一个条目填充 table,这样可以更轻松地从 2 个日期检索数据...
问题是,当用户 edits/deletes 主要事件时,还有一个 table 需要维护...而且同步功能从来没有这么好用过 :/
- 为此类任务获取 hold/retrieve 数据的最佳方式是什么?
- 传播到日常活动中是最好的主意吗?它会以指数方式增长 table 不是吗?但是没有什么像设计良好的索引 table 不会关心这些,也许所有超过 5 年的事件都可以 "archived" 进入特殊的 table...
你怎么看?
已添加
为了让问题更简单,这就是我要深入研究的问题
如何查询得到:
events 1, 2, 4 when query day 3
events 1, 2, 3 when query day 5
events 1, 2, 3 when query from days 4 to 7
events 1, 2 when query from days 10 to 14
这里有一个测试 ms-sql 可以玩:
Hostname 3b5a01df-b75c-41f3-9489-a8ad01604961.sqlserver.sequelizer.com
Database db3b5a01dfb75c41f39489a8ad01604961
Username czhsclbcolgeznij
Password WrXF2Eozi2qWHAeYejFqRn8cPfQqZyh3FS2JteUHPZHnmoDhwxgVaeMJrVYUX6HR
在 pseudo-sql 中,要查找时间段的交集,您可以使用
select
*
from events
where startdate<=[end of day 3]
and enddate>=[start of day 3]
好吧,任务无法在 SQL 中完全解决(从期望的输出来看,我猜你已经意识到了这一点)。另外,我不会给出确切的解决方案,因为它可能过于宽泛,但如果有任何问题,我很乐意提供帮助。我查看了您的帐户,发现您在 C# 中有金色徽章,所以如果有任何建议,我会参考这种语言。
首先,你想要的对象很复杂,所以让我假设这样的模型:
public class Summary
{
public List<string> events { get; set; }
public Dictionary<char, decimal> daysUsed { get; set; }
= new Dictionary<char, decimal> { { 'I', 0 }, { 'V', 0 }, { 'P', 0 } };
}
我认为您关于存储事件的第一个建议更好,我选择了这种方法。
DDL
declare @table table (evtId int, userId int, startDate datetime, endDate datetime, eventType char(1), [description] varchar(1000))
insert into @table values
(1 , 1 , '2017-12-28 00:00:00' , '2018-01-14 23:59:59' , 'V' , 'vacation'),
(2 , 1 , '2018-01-20 00:00:00' , '2018-01-20 23:59:59' , 'I' , 'devOps event'),
(3 , 1 , '2018-01-24 00:00:00' , '2018-01-24 12:00:00' , 'P' , 'haircut')
现在,让我们来看看你想做什么:
首先,声明您想要摘要的期间 - 它可以在 C# 中并通过一些 SQL 方法或一些 ORM 传递到 DB。为简单起见,我使用了 SQL:
declare @startDate datetime, @endDate datetime
select @startDate = '2018-01-01 00:00:00.000', @endDate = '2018-01-31 23:59:59.999'
哪个是你的例子(1 月 1 日至 31 日期间)。
现在让我们尝试获取我们需要的信息:
首先,最重要的部分是定义CTE,它将执行任务:
获取在给定时间段(一月)内开始或结束的所有事件。注意:部分在给定时间段内的事件也将包括在内。
我们不需要给定时间段以外的任何数据,因此在 CTE 中,我们将用 1 月 1 日代替开始日期,即事件在给定时间段之前开始的时间。此外,我们将用结束日期代替,当活动在给定时间段后结束时,即 1 月 31 日
CTE:
;with cte as (
select evtId,
userId,
case when startDate < @startDate then @startDate else startDate end [startDate],
case when endDate > @endDate then @endDate else endDate end [endDate],
eventType,
[Description]
from @table
where (startDate between @startDate and @endDate) or
(endDate between @startDate and @endDate)
)
现在,我们从您的 table 中获得了“干净”的数据。查询时间:
对于第一部分,即 events: [{...}, {...}, {...}]
,我想下面的查询就足够了:
select [description] from cte
此查询的结果可以存储在 Summary
class 的 events
属性 中。
现在,我们需要填充 daysUsed
属性:
select eventType,
round(1.0 * sum(datediff(minute, startDate, endDate)) / (60 * 24), 2) [daysUsed]
from cte
group by eventType
以上查询将 return 以下数据:
eventType|daysUsed
I |1
P |0.5
V |14
一般来说,上述查询的结果总是:
eventType|daysUsed
I |value
P |value
V |value
这个结果可以很容易地用于填充 daysUsed
Summary
class 中的属性。
最后,您可以将 Summary
class 的对象序列化为 JSON。然后你就会得到想要的输出。
备注:
C# class 只是一个示例,可以帮助我形象化地了解每一步我想做什么。不知道你用的是什么技术
我知道,第二次查询(填充
daysUsed
)的结果与您的略有不同,因为我没有排除周末。我没有排除周末和节假日,因为这不是主要问题,我认为您可以轻松处理。我用的是CTE,只能查询一次,所以严格来说,应该是一些临时的table或者CTE的内部查询可以作为其他查询中的子查询(在我看来这将是最简单的)。
如果我对问题的理解正确,您只是希望 return 在特定时期内活跃的事件。如果那是正确的,那么这是一个相对容易解决的问题。如果满足以下条件,我们可以认为事件在一段时间内处于活动状态:
事件的起始边缘或结束边缘包含在周期内或
事件跨越整个周期,在这种情况下,事件的开始边缘将小于周期的开始时间,事件的结束边缘将大于周期的结束时间。
以下示例演示:
-- -------------------------------------------------------
-- Creat sample table and data
-- -------------------------------------------------------
CREATE TABLE [dbo].[EventData](
[ID] [int] IDENTITY(1,1) NOT NULL,
[EventStart] [datetime] NOT NULL,
[EventEnd] [datetime] NOT NULL,
CONSTRAINT [PK_EventData] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[EventData] ON
GO
INSERT [dbo].[EventData] ([ID], [EventStart], [EventEnd]) VALUES (1, CAST(N'2018-01-01T00:00:00.000' AS DateTime), CAST(N'2018-01-13T00:00:00.000' AS DateTime))
GO
INSERT [dbo].[EventData] ([ID], [EventStart], [EventEnd]) VALUES (2, CAST(N'2018-01-03T00:00:00.000' AS DateTime), CAST(N'2018-01-15T00:00:00.000' AS DateTime))
GO
INSERT [dbo].[EventData] ([ID], [EventStart], [EventEnd]) VALUES (3, CAST(N'2018-01-04T00:00:00.000' AS DateTime), CAST(N'2018-01-06T00:00:00.000' AS DateTime))
GO
INSERT [dbo].[EventData] ([ID], [EventStart], [EventEnd]) VALUES (4, CAST(N'2018-01-02T00:00:00.000' AS DateTime), CAST(N'2018-01-04T00:00:00.000' AS DateTime))
GO
SET IDENTITY_INSERT [dbo].[EventData] OFF
GO
-- -------------------------------------------------------
-- Sample Query
-- -------------------------------------------------------
DECLARE @periodStart DATETIME = '10 Jan 2018'; -- Selection period start date and time
DECLARE @periodEnd DATETIME = '14 Jan 2018'; -- Selection period end date and time
SELECT *
FROM [EventData] [E]
WHERE (
( -- Check if event start time is contained within period
(@periodStart <= [E].[EventStart]) AND (@periodEnd > [E].[EventStart])
)
OR
(
-- Check if event end time is contained within period
(@periodStart < [E].[EventEnd]) AND (@periodEnd > [E].[EventEnd])
)
OR
(
-- Check if the event spans the entire period
(@periodStart >= [E].[EventStart]) AND (@periodEnd <= [E].[EventEnd])
)
)