14

我必须创建类似于谷歌日历的东西,所以我创建了一个包含用户所有事件的事件表。

困难的部分是处理重复发生的事件,事件表中的行有一个 event_type 字段,它告诉您它是什么类型的事件,因为一个事件只能针对一个日期,或者每 x 天重新发生一个事件.

主要的设计挑战是处理重复发生的事件。

当用户使用月份视图查看日历时,如何显示给定月份的所有事件?查询会很棘手,所以我认为创建另一个表并为每个事件创建一行会更容易,包括重新发生的事件。

你们有什么感想?

4

16 回答 16

5

尝试存储每个事件的每个实例似乎真的有问题,而且,嗯,不可能。如果有人创建了一个“每个星期四,永远”发生的事件,那么您显然无法存储所有未来的事件。

可以尝试按需生成未来事件,并仅在必要时填充未来事件以显示它们或发送有关它们的通知。但是,如果您无论如何都要构建“按需”生成代码,为什么不一直使用它呢?无需从事件表中提取,然后必须使用按需事件生成来填充尚未添加到表中的任何新事件,只需专门使用按需事件生成即可。最终结果将是相同的。使用此方案,您只需要存储开始和结束日期以及事件频率。

我看不出有任何方法可以避免按需生成事件,因此我在事件表中看不到该实用程序。如果您为了缓存而想要它,那么我认为您采用了错误的方法。首先,它的缓存很差,因为无论如何您都无法避免按需生成事件。其次,无论如何,您可能应该在更高级别进行缓存。如果要缓存,则缓存生成的页面,而不是事件。

就提高轮询效率而言,如果您仅每 15 分钟轮询一次,并且您的数据库和/或服务器无法处理负载,那么您已经注定要失败。如果您的数据库无法处理更多、更频繁的轮询而不费力气,那么您的数据库将无法处理用户

于 2008-08-15T20:06:09.863 回答
5

如前所述,不要重新发明轮子,只需增强它。

出 VCalendar,它是开源的,并且有 PHP、ASP 和 ASP.Net (C#) 版本!

您还可以查看Day Pilot,它提供了用 Asp.Net 2.0 编写的日历。他们提供了一个精简版,您可以签出,如果它适合您,您可以购买许可证。

更新(9/30/09):

除非当然轮子坏了!此外,如果您愿意,您可以涂上一层闪亮的新油漆(即:制作更好的 UI)。但至少尝试找到一些基础来构建,因为日历系统可能很棘手(重复事件),并且已经完成了数千次。

于 2008-08-15T21:22:01.520 回答
4

我正在解决这个问题,并且在阅读此线程之前,我已经完全间隔了iCalendar ( rfc 2445 ),所以我不知道这将与该线程集成或不会集成得有多好。无论如何,到目前为止我提出的设计看起来像这样:

  • 您不可能存储重复事件的所有实例,至少不能在它们发生之前存储,所以我只有一个表,将事件的第一个实例存储为实际日期、可选的到期时间以及可为空的 repeat_unit 和 repeat_increment 字段来形容重复。对于单个实例,repition 字段为空,否则单位将是“日”、“周”、“月”、“年”,并且增量只是添加到下一次出现的开始日期的单位的倍数。
  • 仅当您需要与模型中的其他实体建立关系时,存储过去的事件似乎是有利的,即使这样,也没有必要在每种情况下都有一个明确的“事件实例”表。如果其他实体已经有日期/时间“实例”数据,那么事件的外键(或多对多的连接表)很可能就足够了。
  • 要做“更改此实例”/“更改所有未来实例”,我计划只是复制事件并使过时的事件过期。因此,要更改单个实例,您将在最后一次出现旧实例时使旧实例过期,为新的、唯一的实例制作一个副本,其中包含所做的更改且不重复,并在下一次重复出现的原始实例的另一个副本中重复到未来。更改所有未来实例是类似的,您只需使原始实例过期并使用更改和重复详细信息制作新副本。

到目前为止,我在此设计中看到的两个问题是:

  1. 它使 MWF 类型的事件难以表示。这是可能的,但会强制用户创建三个单独的事件,这些事件每周在 M、W、F 上单独重复,并且他们想要进行的任何更改也必须分别在每个事件上完成。这类事件在我的应用程序中并不是特别有用,但它确实在模型上留下了一个缺陷,这使得它不像我想要的那样通用。
  2. 通过复制事件进行更改,您打破了它们之间的关联,这在某些情况下可能很有用(或者,它可能只是偶尔会出现问题。)事件表理论上可以包含一个“copied_from”id字段来跟踪一个事件起源,但我还没有完全考虑过这样的事情会有多大用处。一方面,从 SQL 查询父/子层次关系是一件很痛苦的事情,因此收益需要相当大才能超过查询该数据的成本。我想你可以改用嵌套集

最后,我认为可以使用直接 SQL 计算给定时间跨度的事件,但我还没有计算出确切的细节,而且我认为查询通常最终过于繁琐,不值得。但是,为了便于讨论,您可以使用以下表达式来计算给定月份和年份之间的月差:

(:month + (:year * 12)) - (MONTH(occursOn) + (YEAR(occursOn) * 12))

在最后一个示例的基础上,您可以使用 MOD 来确定月份差异是否是正确的倍数:

MOD(:month + (:year * 12)) - (MONTH(occursOn) + (YEAR(occursOn) * 12), repeatIncrement) = 0

无论如何,这并不完美(它不会忽略过期事件,不考虑事件的开始/结束时间等),因此它只是作为一个激励示例。一般来说,虽然我认为大多数查询最终都会过于复杂。您最好查询在给定范围内发生的事件,或者在范围之前不过期,并在代码而不是 SQL 中计算实例本身。如果您真的希望数据库进行处理,那么存储过程可能会让您的生活更轻松。

于 2008-08-22T22:19:10.480 回答
3

我会说从 ical 标准开始。如果您使用它作为您的模型,那么您将能够执行 google 日历、outlook、mac ical(程序)的所有操作,并且几乎可以立即与它们集成。

从那里开始,是时候完善您的 ajax 和 javascript,因为如果没有大量的 ajax 和 javascript,您将无法拥有带有拖放功能和多个日历的华丽 Web ui。

于 2008-08-15T19:14:02.950 回答
2

The brute-force-ish but still reasonable way would be to create a new row in your single events table for every instance of the recurring event, all pointing not to the event preceding it in the series but to the first event in the series. This simplifies selecting and/or deleting all elements in a particular series, since you can select based on parent id. It also allows users to delete individual items from a series without affecting the rest of them.

This query gets you the series that starts on element 3:

SELECT * FROM events WHERE id = 3 OR parentid = 3

To get all items for this month, assuming you'd have a start date and an end date in your events table, all you'd have to do is:

SELECT * FROM events WHERE startdate >= '2008-08-01' AND enddate <= '2008-08-31'

Handling the creation/modification of series programmatically wouldn't be very difficult, but it really would depend on the feature set you want to provide and how you think it'll be used. If you want to differentiate between series and events, you could have a separate series table and a nullable series_id on your events, allowing you the freedom to muck about with individual events while still retaining control over your series.

于 2008-08-16T04:40:32.290 回答
2

您应该有一个开始日期、结束日期和到期日期。单日活动将具有相同的开始日期和结束日期,并且还允许您进行部分日活动。至于重复发生的事件,那么开始和结束日期将是同一天,但有不同的时间,然后你有一个指定重复频率(每天、每周、每月等)的枚举或表。

这使您可以说“此事件每天出现”表示每天,“此事件出现在每周的第二天”表示每周,“此事件出现在每个月的第 5 天”表示每月,“此事件出现在每年的第 215 天”,只要该日期小于到期日期即可。

于 2008-08-15T19:20:32.610 回答
2

达伦,

这就是我实际设计事件表的方式,但是考虑到它,假设我有 10 万个创建事件的用户,这个表会受到很大的打击,特别是如果我要发送电子邮件来提醒人们他们的事件(事件也可以是一天中的特定时间!),所以我可能每 15 分钟轮询一次这个表!

这就是为什么我想创建另一个表来扩展所有事件/重新发生的事件,这样我可以简单地点击该表并获取用户月份的事件视图,而无需执行任何复杂的查询和业务逻辑,并且它将使轮询也更加有效。

问题是,这个辅助表应该是下一天还是下个月?什么更有意义?由于用户可以查看的最大值是月份视图,因此我倾向于使用一个表格来写出给定月份的所有事件。

(当然,对于用户可能对原始事件表进行的任何编辑,我必须维护这个辅助表)。

灿灿,

我实际上已经设计了具有相同功能的它,但我只是指我将如何存储事件,特别是如何处理重复发生的事件。

于 2008-08-15T19:27:27.290 回答
1

根据过去的经验,我会为每个发生的事件创建一个新记录,然后有一个引用前一个事件的列,这样您就可以跟踪一系列事件中的所有事件。

这有两个优点:

  1. 没有复杂的例程来计算下一个活动日期
  2. 可以编辑单个事件而不影响其余事件

希望这能给你一些思考的食物:)

于 2008-08-15T19:19:04.890 回答
1

我必须同意@ChanChan 阅读有关如何存储这些东西的 ical 规范。没有简单的方法来处理重复,尤其是那些有异常的。我已经建立、重建和重建了一个数据库来处理这个问题,并且我不断地回到 ical。

根据用例创建从属表不是一个坏主意。准确计算发生时间的算法。. . 嗯,发生。. . 确实可以很复杂。运行它是不可避免的,但值得考虑缓存结果。

于 2008-08-15T19:22:35.397 回答
1

@GateKiller

我没有想到您编辑个别事件的情况。在这种情况下,您将单独存储事件是有道理的。

但是,当您这样做时,您将事件存储在多远的将来?你选择任意日期吗?您是否会在用户第一次浏览未来几个月/几年时自动生成新事件?

此外,您如何处理用户想要编辑整个系列的情况。“我们每周二早上 10:30 开会,但我们将在周三 8 点开始开会”

于 2008-08-15T19:25:04.720 回答
0

That is how I have designed my events table actually, but when thinking about it, say I have 100K users who have create events, this table will be hit pretty hard, especially if I am going to be sending out emails to remind people of their events (events can be for specific times of the day also!), so I could be polling this table every 15 minutes potentially!

Databases do exception jobs of handling sets of data, so i wouldn't be too worried about that. What you should do is use this as your primary table, and then as events expire then move them into another table (like an archive).

The next thing is you want to try is to query the db as little as possible, so move the information into a caching tier (like velocity) and just persist data to the database.

Then, you can partition the information across multiple databases for scaling purposes. ie users 1-10000 calendars exist on server 1, 10001 - 20000 exist on server 2, etc.

That's how i would scale a solution like this, but i still think the original solution i proposed is the way to go, it's just how you scale it that becomes the question.

于 2008-08-16T04:22:20.503 回答
0

Ra-Ajax Calendar starter-kit提供了一个处理 RenderDate 事件的示例,该事件可以专门修改日期。虽然“重复事件”更像是一种算法,但在这里我怀疑很少有日历会对你有多大帮助......

于 2008-11-25T01:01:24.437 回答
0

我想我理解您的第二段意味着您正在考虑第二个事件表,该表在每次发生事件时都有一行。我会避免这种情况。

重复发生的事件应该有一个开始日期和一个停止日期(对于每 X 天“永远”持续一次的事件,这可能是 Null)你必须决定你想要允许什么样的频率 - 每 X 天,每个月的第 N 天,每个给定的工作日等。

我可能倾向于使用两张桌子 - 一张用于一次性事件,另一张用于重复事件。您必须分别查询和显示两者。

如果我要解决这个问题(并且我会尽我所能避免重新发明这个轮子),我会寻找开源库,或者至少,你可以查看带有日历的开源项目. 有什么建议吗?

于 2008-08-15T19:17:51.803 回答
0

未定义写道:

…这张桌子会受到很大的打击,特别是如果我要发送电子邮件来提醒人们他们的事件(事件也可以是一天中的特定时间!),所以我可能每 15 分钟轮询一次这张桌子!

为通知创建一个表。仅轮询它。

更新事件(重复或其他)时更新通知表。

编辑:如果这是一个问题,数据库视图可能不会违反正常形式。但是,您可能想要跟踪哪些通知已发送,哪些尚未发送到某个地方。

于 2008-08-15T21:44:55.423 回答
0

Derek Park,我将在表中创建事件的每个实例,并且该表将每月重新生成(因此任何设置为“永远”重复发生的事件都将使用 Windows 服务提前一个月重新生成或也许在 sql server 级别)。投票不仅每 15 分钟进行一次,而且可能只针对与电子邮件通知相关的投票。当有人想查看他们一个月的事件时,我将不得不提取他们所有的事件,并重新发生事件并找出要显示的事件(因为重新发生的事件可能是在 6 个月前创建的,但与用户正在查看的一个月)。

Zack,我不太关心拥有一个完美规范化的数据库,我正在考虑创建一个辅助表的事实已经违反了规则之一,呵呵。我的核心数据库表遵循“规则”,但有时我不介意创建辅助表/列,这有助于提高性能。

于 2008-08-15T22:08:13.627 回答
0

如果有人在做 Ruby,那么有一个很棒的库 Runt 可以做这种事情。值得一试。 http://runt.rubyforge.org/

于 2009-02-01T17:15:07.417 回答