Временный шаблон календаря SQL
Я собираюсь создать систему календаря на основе Mysql, где вы можете повторить шаблон, который позволяет говорить каждый понедельник навсегда и всегда. Он также должен охватывать статические/одноразовые события. Мне интересно, какое решение было бы наиболее логичным (и лучшим) для меня. У меня есть четыре метода, которые мне интересно выбрать между.
Метод # 1
Сделайте функцию, которая принимает параметры from
и to
. Эта функция создаст временную таблицу таблицы, которая импортирует существующее статическое расписание через INSERT ... SELECT
. После этого он будет читать таблицу шаблонов и заполнять временную таблицу через пероиду на основе from
и to
.
Это решение кажется приятным с точки зрения того, что запросы будут проще для сбора данных, и он будет работать бесконечно, поскольку вы можете просто повторно заполнить таблицу в зависимости от того, какой месяц вы загружаете. Мне любопытно, когда это может быть медленным способом сделать это или нет.
Метод # 2
Создайте и соедините данные шаблоны через подзапрос и JOIN
со статическим календарем.
Это кажется довольно раздражающим, поскольку запросы будут намного больше и, вероятно, не будут хорошими вообще (?).
Метод № 3
В основном просто шаблон INSERT
позволяет говорить на один год вперед. Тогда я предполагаю, что работа cron будет заселена, чтобы сделать ее на год вперед всегда.
Это простой способ сделать это, но он чувствует, что много ненужных данных хранится, и это действительно не дает бесконечности, за которой я буду.
Метод # 4 (предложенный Вегером)
Если я правильно понимаю, этот метод будет извлекать шаблон из другого запроса и создавать события после выполнения. Это похоже на мои мысли относительно метода № 1 таким образом, что я считаю простой шаблон для создания нескольких строк.
Однако, если это будет реализовано вне Mysql, я потеряю некоторые функции базы данных, которые я буду после.
Надеюсь, вы, ребята, поняли мою ситуацию, и если бы вы могли предложить либо дать, либо аргументировать, почему это лучшее, или дать другое решение.
Лично мне нравится метод # 1 больше всего, но мне любопытно, если он будет отставать от записи таблицы календаря каждый раз.
Ответы
Ответ 1
Я раньше строил этот календарь. Я нашел лучший способ сделать это - подойти к нему так, как планируют кроны. Поэтому в базе данных создайте поле для минуты, часа, дня месяца, месяца и дня недели.
Для случая каждую пятницу в июне и августе в 10:00 ваша запись будет выглядеть как
Minute Hour DayOfMonth Month DayOfWeek
0 22 * 6,8 5
Тогда у вас может быть поле, которое обозначает его как одноразовое событие, которое будет игнорировать эту информацию и просто использовать дату и продолжительность начала. Для событий, которые повторяются в конце концов (скажем, каждые выходные в течение 3 месяцев), вам просто нужно добавить поле даты окончания.
Это позволит вам легко выбрать его и уменьшить объем данных, которые необходимо сохранить. Это упрощает ваши запросы.
Я не думаю, что есть необходимость создавать временные таблицы. Чтобы выбрать соответствующие события, вы можете выбрать их по календарю. Если ваше представление календаря по месяцам, ваш выбор будет выглядеть примерно так:
SELECT Events.*
FROM Events
WHERE (Month LIKE '%,'.$current_month.',%' OR Month = '*')
AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'"
AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"
Очевидно, что это должно быть в подготовленном заявлении. Он также предполагает, что у вас есть запятая до и после первого и последнего значения в списке месяцев с запятой (т.е. ,2,4,6,
). Вы также можете создать таблицу Month
и таблицу соединений между ними, если хотите. Остальное можно разделить php при рендеринге вашего календаря.
Если вы показываете недельный вид своего календаря, вы можете выбрать таким образом:
SELECT Events.*
FROM Events
WHERE (DayOfMonth IN ('.implode(',', $days_this_week).','*')
AND (Month LIKE '%,'.$current_month.',%' OR Month = '*'))
AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'"
AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"
Я не тестировал эти запросы, так что, возможно, некоторые испорченные скобки или что-то еще. Но это была бы общая идея.
Таким образом, вы можете либо запустить выбор для каждого дня, который вы показываете, либо выбрать все для просмотра (месяц, неделя и т.д.) и циклически перебирать события за каждый день.
Ответ 2
Мне нравится решение Veger наилучшим образом. Вместо заполнения нескольких строк вы можете просто заполнить шаблон. Я предлагаю формат crontab
.. он работает так хорошо в любом случае.
Вы можете запросить все шаблоны для данного клиента, когда они загружают календарь и заполняют события на основе шаблона. Если у вас нет тысячи шаблонов для одного пользователя, это не должно быть настолько медленным. Он также должен быть быстрее, чем хранить большое количество событий строки в течение длительного времени. Вам нужно будет сразу выбрать все шаблоны и выполнить некоторую предварительную обработку, но еще раз, сколько моделей вы ожидаете от пользователя? Даже 1000 или около того должны быть довольно быстрыми.
Ответ 3
У меня была эта идея, так как я все еще программировал в GW Basic;-), хотя в то время я взял вариант №3, и все. Оглядываясь назад, а также некоторые другие ответы, это было бы моим текущим решением.
структура таблицы
start (datetime)
stop (datetime, nullable)
interval_unit ([hour, day, week, month, year?])
interval_every (1 = every <unit>, 2 every two <units>, etc.)
type ([positive (default), negative]) - will explain later
Дополнительные поля:
title
duration
Поле type
определяет, как обрабатывается событие:
- положителен; нормальное лечение, оно отображается в календаре
- отрицательна; это событие отменяет другой (например, каждый понедельник, но не на 14-м).
вспомогательный запрос
Этот запрос сужает события, чтобы показать:
SELECT * FROM `events`
WHERE `start` >= :start AND (`stop` IS NULL OR `stop` < :stop)
Предполагая, что вы запрашиваете диапазон только по дате (без компонента времени), значение :stop
должно быть на один день раньше вашего диапазона.
Теперь для различных событий, которые вы хотите обработать.
одно событие
start = '2012-06-15 09:00:00'
stop = '2012-06-15 09:00:00'
type = 'positive'
Событие происходит один раз в 2012-06-15 в 9:00
ограниченное повторяющееся событие
start = '2012-06-15 05:00:00'
interval_unit = 'day'
interval_every = 1
stop = '2012-06-22 05:00:00'
type = 'positive'
События происходят каждый день в 5 утра, начиная с 2012-06-15; последнее событие находится на 22-м
неограниченное повторяющееся событие
start = '2012-06-15 13:00:00'
interval_unit = 'week'
interval_every = 2
stop = null
type = 'positive'
События происходят каждые две недели в 13:00, начиная с 2012-06-15
повторяющееся событие с исключениями
start = '2012-06-15 16:00:00'
interval_unit = 'week'
interval_every = 1
type = 'positive'
stop = null
start = '2012-06-22 16:00:00'
type = 'negative'
stop = '2012-06-22 16:00:00'
События происходят каждую неделю в 16:00, начиная с 2012-06-22; но не на 22-м
Ответ 4
Я бы предложил что-то вроде этого:
Разделите таблицу событий на 2, потому что есть явно 2 разных типа повторяющихся событий и статических событий, и в зависимости от типа они будут иметь разные атрибуты.
Затем для определенного поиска событий вы будете запускать 2 запроса, по одному против каждого типа события. Для таблицы статических событий вам должно потребоваться (по крайней мере) одно поле datetime, поэтому поиск в течение данного месяца будет просто использовать этот feild в условиях (где event_date > FirstDayOfTheMonth и event_date < LastDayOfTheMonth). Та же логика для еженедельного/годового обзора.
Этот набор результатов будет объединен со вторым набором результатов из таблицы повторяющихся событий. Возможные атрибуты могут быть похожими на записи crontab, используя день недели/день месяца в качестве 2 основных переменных. Если вы просматриваете ежемесячное представление,
select * from recurring_events where DayOfWeek in (1,2,3,4,5,6,7) or (DayOfMonth > 0 and DayOfMonth < @NumberOfDaysInThisMonth )
Снова аналогично, если для недельного/годового представления. Чтобы сделать это еще проще для интерфейса, используйте хранимые процедуры со всей логикой для определения "какие дни недели найдены между датой A и датой B".
Как только у вас есть оба набора результатов, вы можете объединить их вместе в клиенте, а затем отобразить их вместе. Преимущество этого заключается в отсутствии необходимости в "макетных/пустых записях" и асинхронных cronjob, которые предварительно заполняются, запросы могут легко выполняться "на лету", и если производительность на самом деле ухудшается, добавьте слой кэширования, особенно для системы этого природа кеш имеет прекрасный смысл.
Ответ 5
Я действительно ищу что-то похожее на это и мое решение до сих пор (на бумаге, которую я еще не начинал структурировать или кодировать) хранится в 2 таблицах:
-
"события" получат дату первого вхождения, заголовок и описание (плюс идентификатор автоинкремента).
-
таблица "events_recursion" будет ссылаться на предыдущую таблицу (например, поле event_id) и может работать двумя возможными способами:
2.A: хранить все вхождения по дате (т.е. одна запись для каждого события, поэтому 4, если вы хотите сохранить "каждую пятницу месяца" или 12 для "1-го числа каждого месяца в 2012 году" )
2.B: или сохранение интервала (я бы сохранил его в секундах) с даты первого события в поле + дата последнего вхождения (или конец рекурсии) в другое поле, например
ID: 2
EVENT_ID: 1
ИНТЕРВАЛ: 604800 (неделя, если я не ошибаюсь)
END: 1356912000 (должно быть, конец этого года)
Затем, когда вы открываете php, который показывает расписание, он будет проверять, что событие все еще активно в этом месяце с соединением между двумя таблицами.
Причина, по которой я бы использовал 2 таблицы с перекрестными ссылками вместо того, чтобы сохранять все в одной таблице, происходит только из фактов, что мои проекты видят очень сумасшедшие события, такие как "каждый пятница и третий понедельник каждого месяца" (что в этом case будет 1 запись в таблицах событий и 2 с тем же поле "event_id" во второй таблице. BTW мои проекты предназначены для учителей музыки, которые здесь получили небольшую работу по строгим расписаниям, которые решаются 3 или 6 месяцев за раз и являются реальным беспорядком).
Но, как я уже сказал, я еще не начал, поэтому я с нетерпением жду вашего решения.
PS: пожалуйста, простите (и забудьте) мой английский, сначала не мой язык, а во-вторых, это довольно поздно, и я сонный
Ответ 6
Возможно, посмотрите отличные идеи из События MySQL
и еще несколько:
http://phpmaster.com/working-with-mysql-events/?utm_source=rss&utm_medium=rss&utm_campaign=phpmaster-working-with-mysql-events
http://dev.mysql.com/doc/refman/5.5/en/create-event.html
Ответ 7
лучшее решение зависит от того, хотите ли вы одобрить стандартное соответствие (RFC5545) или работать исключительно в MySQL.
зависит от гибкости вашего механизма правил повторения. Если вам нужны простые правила (каждый 1-й месяц или каждый январь...), то предлагаемые выше решения были подробно описаны.
Однако, если вы хотите, чтобы ваше приложение предлагало совместимость с существующими стандартами (RFC5545), которое включает в себя гораздо более сложные правила, вы должны посмотреть на это сообщение SOA при создании приложения календаря, Я храню даты или правила повторения в моей базе данных?
Ответ 8
Я сделал бы это, как я объяснил здесь. Это создаст бесконечный каландр:
PHP/MySQL: повторяющиеся события модели в базе данных, но запросы для диапазонов дат
Недостатком является то, что во время запроса будут выполняться некоторые вычисления. Если вам нужен высокопроизводительный веб-сайт, предварительная загрузка данных будет способом. Вам даже не нужно предварительно загружать все события в календаре, чтобы можно было легко изменять значения в одном событии. Но было бы разумно хранить все даты с этого момента до...
Теперь использование кешированных значений делает его менее бесконечным, но оно увеличит скорость.
Копия тента для легкого доступа:
Я бы создал таблицу таблиц всего за один столбец с именем id
и заполнил эту таблицу цифрами от 0 до 500. Теперь мы легко используем это, чтобы делать выбор вместо использования цикла while.
Id
-------------------------------------
0
1
2
etc...
Затем я буду хранить события в таблице с помощью Name as varchar
, startdate as datetime
и repeats as int
Name | StartDate | Repeats
-------------------------------------
Meeting | 2012-12-10 00:00:00 | 7
Lunch | 2012-12-10 00:00:00 | 1
Теперь мы можем использовать таблицу подсчета, чтобы выбрать все даты между двумя датами, используя:
SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC
ShowDate
-------------------------------------
2012-12-09 00:00:00
2012-12-10 00:00:00
2012-12-11 00:00:00
2012-12-12 00:00:00
2012-12-13 00:00:00
2012-12-14 00:00:00
2012-12-15 00:00:00
2012-12-16 00:00:00
2012-12-17 00:00:00
2012-12-18 00:00:00
2012-12-19 00:00:00
2012-12-20 00:00:00
Затем мы присоединяем это к таблице событий, чтобы вычислить разницу между стартовой и показательной. Мы разделили результаты этого на столбец repeats
, и если остаток 0
, мы имеем совпадение.
Все вместе:
SELECT E.Id, E.Name, E.StartDate, E.Repeats, A.ShowDate, DATEDIFF(E.StartDate, A.ShowDate) AS diff
FROM events AS E, (
SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC
) a
WHERE MOD(DATEDIFF(E.StartDate, A.ShowDate), E.Repeats)=0
AND A.ShowDate>=E.StartDate
В результате получается
Id | Name |StartDate | Repeats | ShowDate | diff
---------------------------------------------------------------------------------
1 | Meeting | 2012-12-10 00:00:00 | 7 | 2012-12-10 00:00:00 | 0
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-10 00:00:00 | 0
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-11 00:00:00 | -1
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-12 00:00:00 | -2
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-13 00:00:00 | -3
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-14 00:00:00 | -4
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-15 00:00:00 | -5
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-16 00:00:00 | -6
1 | Meeting | 2012-12-10 00:00:00 | 7 | 2012-12-17 00:00:00 | -7
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-17 00:00:00 | -7
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-18 00:00:00 | -8
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-19 00:00:00 | -9
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-20 00:00:00 | -10
Теперь вы могли (и должны!) ускорить процесс. Например, непосредственно сохраняя даты в таблице, чтобы вы могли просто выбрать все даты непосредственно, а не использовать таблицу с таблицей dateadd. Все, что вы можете кэшировать и не нужно вычислять снова, хорошо.