Каков наилучший способ хранения дней недели, когда событие происходит в реляционной базе данных?
Мы пишем продукт для управления записями для школ, и одним из требований является способность управлять расписаниями курсов. Я не смотрел код, как мы справляемся с этим (сейчас я нахожусь в другом проекте), но тем не менее я начал задаваться вопросом, как лучше всего справиться с одной частью этого требования, а именно, как справиться с тем, что каждый курс может проводиться один или несколько дней недели и как лучше всего хранить эту информацию в базе данных. Чтобы обеспечить некоторый контекст, таблица bare-bones 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
(хотя вы можете справиться с этим на уровне приложения или создать представления и хранимые процедуры в базе данных, чтобы упростить это), breaks реляционной базы данных.
Возможное решение # 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
просто будет внешним ключом в этой таблице поиска. Тем не менее, это решение кажется самым неэлегантным, и я только рассматривал его, потому что это делалось так, как это делалось в Relational Way TM.
Преимущества: это единственное решение, которое является "чистым" с точки зрения реляционной базы данных.
Против: он неэлегантный и громоздкий. Например, как бы вы создали пользовательский интерфейс для назначения соответствующих будних дней данному курсу вокруг таблицы поиска? Я сомневаюсь, что пользователь хочет иметь дело с выборами по строкам "воскресенье", "воскресенье, понедельник", "воскресенье, понедельник, вторник", "воскресенье, понедельник, вторник, среда" и т.д....
Другие идеи?
Итак, есть ли более элегантный способ обработки нескольких значений в одном столбце? Или один из предложенных решений будет достаточным? Для чего это стоит, я думаю, что мое второе решение, вероятно, является лучшим из трех возможных решений, которые я изложил здесь, но мне было бы интересно узнать, есть ли у кого-то другое мнение (или вообще совсем другой подход).
Ответы
Ответ 1
Я бы избегал строковой опции для чувства чистоты: она добавляет дополнительный уровень кодирования/декодирования, который вам не нужен. Это может также испортить вас в случае интернационализации.
Поскольку количество дней в неделю равно 7, я бы сохранил семь столбцов, возможно, логических. Это также облегчит последующие запросы. Это также будет полезно, если инструмент когда-либо используется в странах, где рабочая неделя начинается в разные дни.
Я бы избежал поиска, потому что это было бы слишком нормальным. Если ваш набор элементов поиска не является очевидным или, возможно, может измениться, он будет излишним. В случае дней недели (в отличие от США, например), я бы спокойно спал с фиксированным набором.
Учитывая область данных, я не думаю, что бит-билд обеспечит вам значительную экономию пространства и просто сделает ваш код более сложным.
Наконец, слово предупреждения о домене: многие школы делают странные вещи со своими графиками, где они "меняют дни", чтобы сбалансировать равное количество будних дней каждого типа в течение семестра, несмотря на праздники. Я не совсем понимаю вашу систему, но, пожалуй, наилучшим подходом было бы сохранить таблицу фактических дат, в которых предполагается, что курс будет проходить. Таким образом, если есть два вторника в неделю, учителю можно заплатить за то, что он дважды появился, а учитель в четверг, который был отменен, не заплатит.
Ответ 2
Мне не сложно записывать запросы, если мы используем бит. Просто используйте простую двоичную математику. Я думаю, что это самый эффективный метод. Лично я делаю это все время. Посмотрите:
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
Если вы хотите, чтобы курсы на thu и fri вы писали:
select * from courses where (days & 48) > 0
Эта статья актуальна: http://en.wikipedia.org/wiki/Bitwise_operation
вы можете поместить числа дней недели, как константы в свой код, и это будет достаточно ясно.
Надеюсь, что это поможет.
Ответ 3
Возможное # 4: Почему это должен быть один столбец? Вы можете добавить 7 бит столбцов для каждого дня недели в таблицу. Написание SQL против него просто, просто проверьте для 1 в столбце по вашему выбору. И чтение кода приложения из базы данных просто скрывает это в коммутаторе. Я понимаю, что это ненормальная форма, и я обычно трачу довольно много времени, пытаясь отменить такие проекты у предыдущих программистов, но я несколько сомневаюсь, что мы собираемся добавить восьмой день на неделю в ближайшее время.
Чтобы прокомментировать другие решения, я, вероятно, стонал бы, если бы столкнулся с таблицей поиска. Моим первым наклонением было также поле бит с несколькими пользовательскими функциями базы данных, которые помогут вам легко писать естественные запросы в этом поле.
Мне будет интересно прочитать некоторые другие предложения, которые приходят людям.
Изменить: я должен добавить, что # 3 и вышеприведенное предложение легче добавить индексы. Я не уверен, как можно написать SQL-запрос, например "получить все классы в четверг" для запросов №1 или №2, которые не приведут к сканированию таблицы. Но сегодня я могу быть тусклым.
Ответ 4
Решение № 3, по-видимому, ближе всего к тому, что я бы рекомендовал. Расширение идеи таблицы поиска. Каждый курс имеет одну или несколько сеансов. Создайте таблицу сеанса с атрибутами: course_id, day, time, lecturer_id, room_id и т.д.
Теперь у вас есть возможность назначить другого лектора или комнату для каждой сессии каждого курса, предполагая, что вы захотите сохранить эти данные позже.
Проблемы с пользовательским интерфейсом не актуальны, если вы рассматриваете лучший дизайн базы данных. Вы всегда можете создавать представления для отображения данных, а для захвата данных ваше приложение может позаботиться о логике захвата многих сеансов для каждого курса и добавления их в базу данных.
Значение таблиц будет более четким, что упростит долгосрочное обслуживание.
Ответ 5
Если вы выберете один или два, ваша таблица не будет в 1NF (первая нормальная форма), так как она содержит многозначный столбец.
У Николаса отличная идея, хотя я бы не согласился с тем, что его идея нарушает первую нормальную форму: данные на самом деле не повторяются, так как каждый день хранится независимо.
Единственная проблема заключается в том, что вам нужно получить больше столбцов.
Ответ 6
Если производительность является проблемой, я бы рекомендовал более чистую разметку # 3.
Свяжите свой курс с таблицей "расписание".
Это, в свою очередь, связано с таблицей days_in_schedule.
В таблице days_in_schedule указаны столбцы имя_расписания и дата in_schedule_day. Строка для каждого действительного дня в этом расписании.
Вам нужно некоторое время для какой-либо умной программы, чтобы заполнить таблицу, но как только это будет сделано
гибкость стоит того.
Вы можете справиться не только с курсом только по пятницам, но также "только в первом семестре", "лаборатория закрыта для ремонта в третьем семестре", а "канадский филиал имеет календарь расписания знакомств".
Другие возможные запросы: "Какая дата окончания 20-дневного курса, начинающегося 1 апреля", который "расписывает больше всего".
Если вы действительно хорошо разбираетесь в SQL, вы можете спросить: "Какие возможные дни открыты в курсе xxx для ученика, который уже забронирован для курса yyy", - что у меня такое чувство, это настоящий кукол вашей предлагаемой системы.