3

我们有一个体育训练营,该市的各个团队定期参加。我们每天有一个 2 小时(上午 9 点到 11 点)的会议,不同团队的时间段可能会有所不同。我们想记录每天参加训练营的人。

我们得出了以下模型来记录出勤率。(id、user_id、日期、现在)。假设用户每天都参加训练营(比如一个月 30 天),您将在数据库中看到这么多记录。

假设我们只对找出用户参加训练营的天数感兴趣,是否有更好的方法来标记特定用户的存在或不存在(可能只有一个月的一行并将所有单独的天标记为类似于 (P,P,P,A, ...,A,P)。P = 存在,A = 不存在

4

4 回答 4

3

您在问题标题中使用了“优化”一词,而没有解释您要优化的内容

如果您在谈论查询性能,那么您没有问题。您可以拥有的记录数量取决于您每天的会话数(因为只有一个团队可以参加任何给定的会话)。如果您每天运行 10 次会话,则每月有 300 条记录。如果您每天运行 100 次会话,则每月运行 3000 条记录。这些不是大量的数据。因此,通过扭曲数据库设计以避免不存在的性能问题,您做出了错误的决定。

您在其中一条评论中提到了电子表格。这不是一个糟糕的设计。顶行是会议,旁边是团队,单元格显示团队是否出席了会议。这些映射到三个数据库表:SESSIONS、TEAMS 和交集表 TEAM_SESSIONS。当团队参加会议时,您只需要 TEAM_SESSIONS 中的记录。

作为概念证明,我在 Oracle 中敲了三个表。

SQL> desc teams
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL NUMBER
 NAME                                               VARCHAR2(20 CHAR)

SQL> desc sessions
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL NUMBER
 SSN_DAY                                            DATE
 SSN_START                                          NUMBER(4,2)
 SSN_END                                            NUMBER(4,2)

SQL> desc team_sessions
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 TEAM_ID                                   NOT NULL NUMBER
 SESSION_ID                                NOT NULL NUMBER

SQL>

Oracle 11g 中引入的 PIVOT 函数使敲击矩阵变得轻而易举(不同风格的 DBMS 将有不同的处理方法)。如您所见,今天有三支球队预定了训练,没有人想在午餐时间训练,而 Bec United 就像芥末一样热衷(或需要训练)!

SQL> select * from (
  2      select t.name as team_name
  3             , trim(to_char(s.ssn_start))||'-'||trim(to_char(s.ssn_end)) as ssn
  4             , case when ts.team_id is not null then 1 else 0 end as present
  5      from   sessions s
  6             cross join teams t
  7             left outer join team_sessions ts
  8                  on (ts.team_id = t.id
  9                      and ts.session_id = s.id )
 10      where s.ssn_day = trunc(sysdate)
 11      )
 12  pivot
 13      ( sum (present)
 14        for ssn in ( '9-11', '11-13', '13-15', '15-17', '17-19')
 15      )
 16  order by team_name
 17  /

TEAM_NAME                '9-11'    '11-13'    '13-15'    '15-17'    '17-19'
-------------------- ---------- ---------- ---------- ---------- ----------
Balham Blazers                0          1          0          0          0
Bec United                    1          0          0          0          1
Dinamo Tooting                0          0          0          0          0
Melchester Rovers             0          0          0          1          0

SQL>

无论如何,这种数据模型的优点是它很灵活。我们可以计算一个团队参加的频率、参加的时间、参加的星期几、总是预订哪些会议、很少预订哪些会议等。此外,还可以轻松管理数据。特别是三桌解决方案相对于两桌的优势在于更容易防止重复预订和不标准或重叠的时间段。

你看,规范化不仅仅是我们用来欺骗无辜者的一些月亮语言,它提供了真正的实际好处。在少数情况下,至少降低到 BCNF 不是最好的主意。

于 2010-07-07T12:39:38.343 回答
2

你应该问问自己为什么要这么做。

有一些可能性,但您的数据库架构很可能不会完全规范化。

所以首先:你想达到什么目标,原因是什么?

一些可能性:

  • 一些 DBMS 提供了创建用户定义类型的能力
  • 您可以使用按位方法(在 mysql 中,最简单的方法是使用SET 数据类型

但是再说一遍:您当前的问题是什么,因为找出某人在场的天数只不过是加入适当的表格,并使用计数功能进行汇总

于 2010-07-07T09:15:46.203 回答
1
AttMst
  id | date

AttDet
  attdetid | id | userid

这样,您需要将日期存储在 AttMst 中,而当天的当前用户将存储在 AttDet 中。

于 2010-07-07T09:18:31.610 回答
0

恕我直言,每个用户每个月有一行包含很多连接字符并不会比有很多行上面有一个字符更好,特别是如果您每次都必须拆分该字符串想要在另一个应用程序上显示数据。

如果您只想计算用户参加您的营地的天数,为什么不专门为此创建一个表格呢?每次您记录用户的出席情况时,您只需通过增加用户出席的天数来更新该表。因此,该值不会即时计算,也不会给您带来任何性能问题。

所以,我的建议包括两个表格:

id | user_id | date | present

user_id | month | attendance

您还应该在 user_id 字段上有一些索引,以提高系统的性能。

干杯

于 2010-07-07T09:03:04.517 回答