Как я могу заказать свои товары по дате и времени с этой запутанной структурой таблицы? - PullRequest
4 голосов
/ 01 июня 2011

Я использую ужасную систему CMS.

Его модуль событий может хранить события с явной датой / временем, например, 2011-08-04 13:30:00 или еженедельным повторяющимся событием, для которого повторяющийся день сохраняется какцелое число, основанное на 0, и время добавляется в поле даты / времени (где данные равны 0), например, 0000-00-00 13:30:00.

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

Я не большой эксперт по SQL.Я написал этот запрос ...

SELECT `id`,
       `name`,
       `info`,
       `date_time`,
       `weekly_day`
FROM   `events` AS a
WHERE  `active` = true
       AND `id` IN (SELECT `id`
                    FROM   `events`
                    WHERE  WEEK(`date_time`) BETWEEN WEEK(NOW()) AND WEEK(
                                                     DATE_ADD(NOW(), INTERVAL
                                                     1 WEEK)))
        OR DATE(`date_time`) = 0
ORDER  BY Time_to_sec(IF(TIME(`date_time`), TIME(`date_time`),
                                            Concat(EXTRACT(HOUR FROM `date_time`
                                                   ), ":",
                                            EXTRACT(MINUTE FROM `date_time`)))),
          IF(DATE(`date_time`), Dayofweek(`date_time`), `weekly_day` + 1) ASC 

(Обратите внимание, что NOW() выше заменены на некоторые PHP с отражением текущей даты в формате MySQL. Часовой пояс сервера MySQL отличается от часового пояса, который я хочу.)

Это успешно получает все записи, которые я хочу, но не упорядочивает их правильно.Например, события 9 июня появляются перед событиями 2 июня.

Как исправить запрос?

Cheers.

Обновление

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

SELECT `id`,
       `name`,
       `info`,
       `date_time`,
       `weekly_day`,
       ( DATE(`date_time`) = 0 ) AS `weekly`
FROM   `events`
WHERE  `active` = true
       AND IF (DATE(`date_time`), WEEK(`date_time`),
           DATE_ADD(
               Concat(DATE(NOW()), " ", Concat(EXTRACT(HOUR FROM `date_time`),
                                        ":",
                                        EXTRACT
                                        (
                                        MINUTE FROM `date_time`))), INTERVAL
           `weekly_day`
                                                                    DAY))
           BETWEEN WEEK(
           NOW()) AND WEEK(DATE_ADD(NOW(), INTERVAL 2 WEEK))
ORDER  BY `date_time` ASC 

Я не мог использовать TIME() дляизвлеките часть времени, потому что она продолжала возвращаться NULL.

Что я делаю не так?

1 Ответ

3 голосов
/ 01 июня 2011

Я бы сделал это в два шага:

  1. Преобразуйте повторяющиеся записи в два значения DATETIME на следующие две недели.Это будет один подзапрос.
  2. Собрать неповторяющиеся записи за тот же период.Это будет второй подзапрос.

СОЮЗ двух подзапросов дает вам все события с их фактической датой и временем возникновения;упорядочить это правильно тривиально.

Если вам необходимо записать, является ли информация повторяющейся встречей, сохраните что-то (возможно, просто 'R' или 'N') в списке выборадва подзапроса, чтобы вы могли определить, была ли исходная запись повторяющейся или неповторяющейся.


Я не эксперт в манипулировании MySQL DATETIME - но я знаю, что меня нет в этой областидругая СУБД, а именно Informix.Для моих целей в этой дискуссии в таблице есть только два интересных столбца: date_time и weekly_day.Я предполагаю, что вы используете соглашение MySQL, что 1 = воскресенье, 7 = суббота.Поскольку нам требуется информация за 2 недели для повторяющихся событий, вероятно, проще всего сгенерировать 3 недели и отфильтровать несущественные.

С учетом контрольной даты и целого дня недели мы можем получитьтри значения из него с использованием:

 refdate - DAYOFWEEK(refdate) + day_of_week
 refdate - DAYOFWEEK(refdate) + day_of_week + INTERVAL  7 DAYS
 refdate - DAYOFWEEK(refdate) + day_of_week + INTERVAL 14 DAYS

Эти дают три даты;первый может быть в прошлом или третий может быть слишком далеко в будущем (и, следовательно, нужно отказаться).Это арифметика, когда даты - это количество дней с контрольной даты (как в Informix).Чтобы перевести третью строку в MySQL, предполагая, что 'refdate' равен NOW(), нам, кажется, нужно написать (необычно многословно - я думал, что Informix был очень многословен для манипуляции DATETIME):

DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())), INTERVAL weekly_day DAY),
    INTERVAL 14 DAY)

Здесь яЯ предполагаю, что я могу привести целое число в weekly_day к числу дней;отрегулируйте при необходимости.Эти формулы дают три значения DATE для каждой из повторяющихся записей, из которых два являются релевантными.Чтобы добавить время, я думаю, нам нужно:

DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())), INTERVAL weekly_day DAY),
    INTERVAL 14 DAY), INTERVAL TIME(date_time) HOUR_SECOND)

Итак, первый подзапрос, упомянутый в моем первоначальном ответе, должен быть трехсторонним СОЮЗОМ.

SELECT 'R' AS info_mode,
       DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                  INTERVAL weekly_day DAY),
                         INTERVAL 14 DAY),
                INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
  FROM events
UNION
SELECT 'R' AS info_mode,
       DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                  INTERVAL weekly_day DAY),
                         INTERVAL 7 DAY),
                INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
  FROM events
UNION
SELECT 'R' AS info_mode,
       DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                  INTERVAL weekly_day DAY),
                INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
  FROM events;

Теперь вам нужно отфильтровать это в течение «следующих двух недель».Какой диапазон даты / времени это точно?От эталонного момента - назначенный NOW ()?Или с начала справочного дня?Или с начала дня после контрольного дня?Все может быть правдоподобным;поскольку код, по-видимому, использует эталонный режим (и он самый простой), мы пойдем с этим:

SELECT *
  FROM (SELECT 'R' AS info_mode,
               DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                          INTERVAL weekly_day DAY),
                                 INTERVAL 14 DAY),
                        INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
          FROM events
        UNION
        SELECT 'R' AS info_mode,
               DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                          INTERVAL weekly_day DAY),
                                 INTERVAL 7 DAY),
                        INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
          FROM events
        UNION
        SELECT 'R' AS info_mode,
               DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                          INTERVAL weekly_day DAY),
                        INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
          FROM events) AS r
  WHERE r.event_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 14 DAY);

Это дает повторяющиеся события.Состояние фильтра на r.event_time может быть перенесено (реплицировано) в каждую ветвь UNION, но я оставлю его там, где оно есть, временно.

Неповторяющиеся события находят с помощью:

SELECT 'N' AS info_mode,
       date_time AS event_time
  FROM events AS e
 WHERE e.date_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 14 DAY)
   AND DATE(e.date_time) != DATE '0000-00-00';

Следовательно, мы можем создать объединение этих двух запросов.Можно перетащить условие на event_time из двух подзапросов (и мы можем надеяться, что оптимизатор затем «вернет его»).И подзапрос непериодических событий просто становится четвертой частью UNION, что приводит к:

SELECT *
  FROM (SELECT 'R' AS info_mode,
               DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                          INTERVAL weekly_day DAY),
                                 INTERVAL 14 DAY),
                        INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
          FROM events
        UNION
        SELECT 'R' AS info_mode,
               DATE_ADD(DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                          INTERVAL weekly_day DAY),
                                 INTERVAL 7 DAY),
                        INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
          FROM events
        UNION
        SELECT 'R' AS info_mode,
               DATE_ADD(DATE_ADD(DATE_SUB(NOW(), DAYOFWEEK(NOW())),
                                          INTERVAL weekly_day DAY),
                        INTERVAL TIME(date_time) HOUR_SECOND) AS event_time
          FROM events
        UNION
        SELECT 'N' AS info_mode,
               date_time AS event_time
          FROM events
         WHERE DATE(e.date_time) != DATE '0000-00-00'
       ) AS e
 WHERE e.event_time BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 14 DAY);

Итак, это должно дать вам список типов событий (повторяющихся, неповторяющихся) ите, которые происходят в ближайшие 14 дней.Вам просто нужно добавить обратно столбцы id, name и info (плюс, для отладки, необработанные столбцы date_time и weekly_day) к каждому из 4 запросов в UNION и ORDER BYe.event_time (и любые столбцы разрыва связей, которые вы хотите добавить).

Пожалуйста, будьте любезны с синтаксическими или специфическими для СУБД семантическими ошибками;Я надеюсь, что концепция ясна (и правильна), даже если есть детали, которые не верны из-за незнания деталей MySQL.

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