【问题标题】:What is the best way to optimize schema for capturing attendance data优化模式以捕获考勤数据的最佳方法是什么
【发布时间】:2010-07-07 08:47:18
【问题描述】:

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

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

假设我们只对了解用户参加夏令营的天数感兴趣,有没有更好的方法来标记特定用户的存在或不存在(可能只需要一个月的一行并标记所有个别日子,例如 (P,P,P,A, ...,A,P)。P = 现在,A = 缺席

【问题讨论】:

    标签: database-design scheduling time-and-attendance


    【解决方案1】:

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

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

    您在其中一个 cmets 中提到了电子表格。这不是一个糟糕的设计。顶行有会议,旁边有团队,单元格显示团队是否出席了会议。这些映射到三个数据库表: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 不是最好的主意。

    【讨论】:

      【解决方案2】:

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

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

      那么首先:你想达到什么目标?原因是什么?

      一些可能性:

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

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

      【讨论】:

      • 我同意,一张桌子就够了。数据库服务器速度很快,所以不需要去规范化。
      • @mb14 但是当数据量很大的时候,对数据进行归一化是有好处的。
      • 我不确定我们是否需要很多行来捕获出勤信息(如果您查看包含此类信息的 Excel 表,它就像一个网格,并且您有一组整个月的值一个用户,一个月内的所有日子)。我试图建模。我同意这样一个事实,即在逗号分隔的列表中标记玩家的存在或不存在会很痛苦
      • @Himadari - 如果这些表中的行数偏高,您会提出什么建议。我们可以建立什么样的翻转机制,以便以不同的方式存储较旧的数据(比如超过一年)。任何想法
      • @Samuel 我不知道一些不同的方法,只是您可以备份旧数据并从当前数据库中删除。但我说的是规范化表。
      【解决方案3】:
      AttMst
        id | date
      
      AttDet
        attdetid | id | userid
      

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

      【讨论】:

      • 我正在转向这个模型。如何查询一个月内所有用户的考勤记录?您将如何与 AttMst 表进行联接?
      • select date,userid from AttMst am,AttDet ad where am.id = ad.id group by date 这将按日期显示当前用户。但它是未经测试的查询,请检查它。
      【解决方案4】:

      恕我直言,每个用户每个月拥有一行包含大量连接字符的行并不比拥有大量行且包含单个字符的行好,尤其是如果您将不得不拆分该行字符串每次你想在另一个应用程序上显示数据。

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

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

      id | user_id | date | present
      

      user_id | month | attendance
      

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

      干杯

      【讨论】:

      • 假设无论何时标记,从表将用于递增或递减。因此,您建议不要使用 count(*) 机制来计算玩家在场的总天数。我说的对吗?
      • 是的,主要是因为它会导致性能问题。但是,如果您有一个快速的服务器或者不希望有大量用户,那么计数机制就可以了。无论哪种方式,忘记串联。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-14
      • 2012-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多