Как лучше всего хранить дни недели, в которые происходит событие, в реляционной базе данных? - PullRequest
32 голосов
/ 24 ноября 2008

Мы пишем продукт управления записями для школ, и одним из требований является возможность управлять расписанием курсов. Я не смотрел на код для того, как мы имеем дело с этим (я сейчас нахожусь в другом проекте), но, тем не менее, я начал задаваться вопросом, как лучше всего выполнить одну конкретную часть этого требования, а именно, как справиться с тем фактом, что Каждый курс может проводиться один или несколько дней недели, и как лучше всего хранить эту информацию в базе данных. Для обеспечения некоторого контекста таблица Course может содержать следующие столбцы:

Course          Example Data
------          ------------

DeptPrefix      ;MATH, ENG, CS, ...
Number          ;101, 300, 450, ...
Title           ;Algebra, Shakespeare, Advanced Data Structures, ...
Description     ;...
DaysOfWeek      ;Monday, Tuesday-Thursday, ...
StartTime       
EndTime           

Что мне интересно, так это лучший способ обработки столбца DaysOfWeek в этом (надуманном) примере? Проблема, с которой я столкнулся, заключается в том, что это многозначное поле: то есть вы можете пройти курс в любой день недели, и один и тот же курс может проходить более одного дня. Я знаю, что некоторые базы данных изначально поддерживают столбцы с несколькими значениями, но есть ли «лучший способ» справиться с этим, если база данных изначально не поддерживает это?

Я уже нашел следующие возможные решения, но мне интересно, есть ли у кого-нибудь что-нибудь лучше:

Возможное решение № 1: рассматривать DaysOfWeek как битовое поле

Это было первое, что пришло мне в голову (я не уверен, хорошо это или нет ...). В этом решении DaysOfWeek будет определяться как байт, а первые 7 бит будут использоваться для представления дней недели (один бит на каждый день). 1 бит будет означать, что класс проводится в соответствующий день недели.

Плюсы : Простота реализации (приложение может работать с битовыми манипуляциями), работает с любой базой данных.

Минусы : сложнее писать запросы, использующие столбец DaysOfWeek (хотя вы могли бы справиться с этим на уровне приложения или создать представления и хранимые процедуры в базе данных, чтобы упростить это), нарушает реляционные модель базы данных.

Возможное решение № 2: Хранить DaysOfWeek в виде строки символов

По сути, это тот же подход, что и использование битового поля, но вместо обработки необработанных битов вы назначаете уникальную букву каждому дню недели, а в столбце DaysOfWeek просто хранится последовательность букв, указывающая, какие дни курс проводится. Например, вы можете связать каждый день недели с односимвольным кодом следующим образом:

Weekday      Letter
-------      ------

Sunday       S
Monday       M
Tuesday      T
Wednesday    W
Thursday     R
Friday       F
Saturday     U

В этом случае курс, проводимый в понедельник, вторник и пятницу, будет иметь значение 'MTF' для DaysOfWeek, тогда как курс, проводимый только по средам, будет иметь значение DaysOfWeek 'W'.

Плюсы : проще справляться в запросах (т. Е. Вы можете использовать INSTR или его эквивалент, чтобы определить, проводится ли класс в определенный день). Работает с любой базой данных, которая поддерживает INSTR или эквивалентную функцию (большинство, я бы предположил ...). Также удобнее для просмотра и с первого взгляда, что происходит в запросах, использующих столбец DaysOfWeek.

Минусы : Единственное реальное «против» - это то, что, подобно подходу с битовыми полями, это нарушает реляционную модель, сохраняя переменное число значений в одном поле.

Возможное решение № 3: использовать таблицу поиска (безобразно)

Другая возможность - создать новую таблицу, в которой будут храниться все уникальные комбинации дней недели, а столбец Course.DaysOfWeek будет просто внешним ключом этой таблицы поиска. Тем не менее, это решение кажется самым нелегким, и я рассматривал его только потому, что казалось, что Реляционный путь TM делает что-то.

Плюсы : Это единственное «чистое» решение с точки зрения реляционной базы данных.

Минусы : Это не элегантно и громоздко. Например, как бы вы разработали пользовательский интерфейс для назначения соответствующих дней недели для данного курса вокруг таблицы поиска? Я сомневаюсь, что пользователь хочет иметь дело с вариантами выбора «воскресенье», «воскресенье, понедельник», «воскресенье, понедельник, вторник», «воскресенье, понедельник, вторник, среда» и т. Д.

Другие идеи?

Итак, есть ли более элегантный способ обработки нескольких значений в одном столбце? Или одного предложенного решения будет достаточно? Что бы это ни стоило, я думаю, что мое второе решение является, вероятно, лучшим из трех возможных решений, которые я изложил здесь, но мне было бы любопытно посмотреть, есть ли у кого-то другое мнение (или вообще иной подход).

Ответы [ 6 ]

20 голосов
/ 12 июня 2014

Я не думаю, что сложно писать запросы, если мы используем битовую опцию. Просто используйте простую двоичную математику. Я думаю, что это самый эффективный метод. Лично я делаю это все время. Взгляните:

 sun=1, mon=2, tue=4, wed=8, thu=16, fri=32, sat=64. 

Теперь, скажем, курс проводится в понедельник, ср и пт. значение для сохранения в базе данных будет 42 (2 + 8 + 32). Тогда вы можете выбрать курсы в среду, как это:

select * from courses where (days & 8) > 0

если вы хотите курсы в четверг и четверг, напишите:

select * from courses where (days & 48) > 0

эта статья актуальна: http://en.wikipedia.org/wiki/Bitwise_operation

вы можете указывать номера дней недели как константы в вашем коде, и это будет достаточно ясно.

Надеюсь, это поможет.

17 голосов
/ 24 ноября 2008

Я бы не стал использовать строковый параметр из соображений чистоты: он добавляет дополнительный уровень кодирования / декодирования, который вам не нужен. Это может также испортить вас в случае интернационализации.

Поскольку число дней в неделе равно 7, я бы оставил семь столбцов, возможно, логических. Это также облегчит последующие запросы. Это также будет полезно, если инструмент когда-либо использовался в странах, где рабочая неделя начинается в разные дни.

Я бы избегал поиска, потому что это было бы чрезмерной нормализацией. Если ваш набор элементов поиска не очевиден или не может измениться, это излишне. В случае дней недели (в отличие, например, от штатов США) я бы спокойно спал с фиксированным набором.

Что касается предметной области, я не думаю, что битовое поле принесло бы вам значительную экономию места и просто усложнило бы ваш код.

Наконец, предупреждение о домене: многие школы делают странные вещи со своим расписанием, где они «меняют дни», чтобы сбалансировать равное количество дней каждого типа в течение семестра, несмотря на выходные. Я не совсем понимаю вашу систему, но, возможно, лучшим подходом было бы сохранить таблицу с фактическими датами, в которые курс должен состояться. Таким образом, если в неделю два вторника, учитель может быть оплачен дважды, а учитель за отмененный четверг не заплатит.

7 голосов
/ 24 ноября 2008

Возможный # 4: Почему это должен быть один столбец? Вы можете добавить 7-битные столбцы для каждого дня недели в таблицу. Написание SQL против этого просто, просто проверьте на 1 в столбце по вашему выбору. И чтение кода приложения из базы данных просто скрывает это в переключателе. Я понимаю, что это не нормальная форма, и я обычно трачу довольно много времени, пытаясь отменить такие конструкции от предыдущих программистов, но я несколько сомневаюсь, что мы добавим восьмой день к неделе в ближайшее время.

Чтобы прокомментировать другие решения, я бы, вероятно, застонал, если бы столкнулся с таблицей поиска. Моим первым уклоном было также битовое поле с несколькими пользовательскими функциями базы данных, которые помогут вам легко писать естественные запросы к этому полю.

Мне будет любопытно прочитать некоторые другие предложения, которые приходят людям.

Редактировать: я должен добавить, что # 3 и предложение выше легче добавлять индексы. Я не уверен, как можно написать SQL-запрос, такой как «получить все классы в четверг» для запросов № 1 или № 2, которые не приведут к сканированию таблицы. Но я, может быть, сегодня буду смутным.

4 голосов
/ 24 ноября 2008

Решение № 3 кажется наиболее близким к тому, что я бы порекомендовал. Расширение идеи поисковой таблицы. Каждый курс имеет одну или несколько сессий. Создайте таблицу сеанса с атрибутами: course_id, day, time, lecturer_id, room_id и т. Д.

Теперь у вас есть возможность назначить отдельного лектора или комнату для каждого сеанса каждого курса, предполагая, что вы, возможно, захотите сохранить эти данные позже.

Проблемы пользовательского интерфейса не имеют значения, если вы рассматриваете лучший дизайн базы данных. Вы всегда можете создать представления для отображения данных, а для сбора данных ваше приложение может позаботиться о логике захвата множества сеансов для каждого курса и добавления их в базу данных.

Значение таблиц будет более понятным, что облегчит долгосрочное обслуживание.

3 голосов
/ 24 ноября 2008

Если вы выберете один или два, ваша таблица не будет в 1NF (первая нормальная форма), поскольку она содержит многозначный столбец.

У Николаса отличная идея, хотя я не согласен с тем, что его идея нарушает первую нормальную форму: данные фактически не повторяются, поскольку каждый день хранится независимо. Единственная проблема в том, что вам нужно получить больше столбцов.

1 голос
/ 24 ноября 2008

Если производительность является проблемой, я бы порекомендовал более чистую вариацию # 3.

Свяжите свой курс с таблицей «график».

Который, в свою очередь, связан с таблицей days_in_schedule.

В таблице days_in_schedule есть столбцы с именем schedule_name и датой in_schedule_day. Со строкой для каждого действительного дня в этом расписании.

Вам нужно некоторое время или какая-нибудь умная программа для заполнения таблицы, но как только это будет сделано, гибкость того стоит.

Вы можете справиться не только с «курсом только по пятницам», но также и «только в первом семестре», «лаборатория закрыта на ремонт в третьем семестре» и «в канадском филиале есть расписание праздничных дней в разных сетях».

Другими возможными запросами являются «Какова дата окончания 20-дневного курса, начинающегося 1 апреля»? Если вы действительно хорошо разбираетесь в SQL, вы можете спросить «какие возможные дни открыты в курсе ххх для студента, который уже записан на курс yyy», - который, как я чувствую, является настоящей куклой вашей предложенной системы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...