Вы используете слово «оптимизировать» в заголовке вопроса, не объясняя что именно вы хотите оптимизировать .
Если вы говорите о производительности запросов, то у вас нет проблем. Количество записей, которое вы можете иметь, зависит от количества сеансов, которые вы проводите каждый день (потому что только одна команда может посетить любой данный сеанс). Если вы проводите десять сеансов в день, это триста записей в месяц. Если вы проводите сто сеансов в день, то это три тысячи записей в месяц. Это не большие объемы данных. Таким образом, вы принимаете неверное решение, искажая дизайн базы данных, чтобы избежать проблемы с производительностью, которой нет.
Вы упомянули электронные таблицы в одном из ваших комментариев. Это неплохой дизайн. В верхней строке расположены сеансы, внизу - команды, а ячейки показывают, присутствовала ли команда на сессии. Они сопоставляются с тремя таблицами базы данных: 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>
Функция PIVOT, появившаяся в Oracle 11g, позволяет легко разбить матрицу (разные варианты СУБД будут по-разному подходить к этому). Как вы можете видеть, три команды забронировали сессию сегодня, никто не хочет тренироваться во время обеда, и Бек Юнайтед увлечен горчицей (или нуждается в обучении)!
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 - не лучшая идея.