您在问题标题中使用“优化”一词而没有解释你想要优化什么.
如果您谈论的是查询性能,那么您就没有问题。您可以拥有的记录数量取决于您每天参加的会议数量(因为只有一个团队可以参加任何给定的会议)。如果您每天运行十个会话,那么每月就有三百条记录。如果您每天运行一百个会话,则每月会产生三千条记录。这些数据量并不大。因此,您通过扭曲数据库设计来避免不存在的性能问题,从而做出了错误的决定。
您在评论中提到了电子表格。这并不是一个糟糕的设计。顶行是会议,下方是团队,单元格显示团队是否出席会议。它们映射到三个数据库表: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 将有不同的方法来实现这一点)。正如您所看到的,三支球队今天已经预订了课程,没有人愿意在午餐时间训练,而贝克联队则非常热衷(或需要训练)!
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 并不是最好的主意。