Подтвердить что ты не робот

Временный шаблон календаря SQL

Я собираюсь создать систему календаря на основе Mysql, где вы можете повторить шаблон, который позволяет говорить каждый понедельник навсегда и всегда. Он также должен охватывать статические/одноразовые события. Мне интересно, какое решение было бы наиболее логичным (и лучшим) для меня. У меня есть четыре метода, которые мне интересно выбрать между.

Метод # 1

Сделайте функцию, которая принимает параметры from и to. Эта функция создаст временную таблицу таблицы, которая импортирует существующее статическое расписание через INSERT ... SELECT. После этого он будет читать таблицу шаблонов и заполнять временную таблицу через пероиду на основе from и to.

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

Метод # 2

Создайте и соедините данные шаблоны через подзапрос и JOIN со статическим календарем.

Это кажется довольно раздражающим, поскольку запросы будут намного больше и, вероятно, не будут хорошими вообще (?).

Метод № 3

В основном просто шаблон INSERT позволяет говорить на один год вперед. Тогда я предполагаю, что работа cron будет заселена, чтобы сделать ее на год вперед всегда.

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

Метод # 4 (предложенный Вегером)

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

Однако, если это будет реализовано вне Mysql, я потеряю некоторые функции базы данных, которые я буду после.


Надеюсь, вы, ребята, поняли мою ситуацию, и если бы вы могли предложить либо дать, либо аргументировать, почему это лучшее, или дать другое решение.

Лично мне нравится метод # 1 больше всего, но мне любопытно, если он будет отставать от записи таблицы календаря каждый раз.

4b9b3361

Ответ 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: пожалуйста, простите (и забудьте) мой английский, сначала не мой язык, а во-вторых, это довольно поздно, и я сонный

Ответ 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. Все, что вы можете кэшировать и не нужно вычислять снова, хорошо.