База данных запросов в недельном интервале - PullRequest
2 голосов
/ 28 сентября 2011

У меня есть база данных со столбцом created_at, содержащим дату и время в формате Y-m-d H:i:s.

Последняя запись даты и времени: 2011-09-28 00:10:02.

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

  1. Первое значение в запросе должно быть самой последней записью даты и времени.
  2. Второе значение в запросе должно быть записью, ближайшей к 7 дням отпервое значение.
  3. Третье значение должно быть записью, ближайшей к 7 дням от второго значения.
  4. REPEAT # 3.

Что я имею в виду«ближе к 7 дням»:

Ниже приведены даты, желаемый интервал - неделя, в секундах в неделю - 604800 секунды.

7 дней спервое значение равно 1316578202 (1317183002-604800)

значение, наиболее близкое к 1316578202 (7 дней), равно ... 1316571974

unix timestamp | Y-m-d H:i:s

1317183002 | 2011-09-28 00:10:02 -> appear in query (first value)
1317101233 | 2011-09-27 01:27:13
1317009182 | 2011-09-25 23:53:02
1316916554 | 2011-09-24 22:09:14
1316836656 | 2011-09-23 23:57:36
1316745220 | 2011-09-22 22:33:40
1316659915 | 2011-09-21 22:51:55
1316571974 | 2011-09-20 22:26:14 -> closest to 7 days from 1317183002 (first value)
1316499187 | 2011-09-20 02:13:07
1316064243 | 2011-09-15 01:24:03
1315967707 | 2011-09-13 22:35:07 -> closest to 7 days from 1316571974 (second value)
1315881414 | 2011-09-12 22:36:54
1315794048 | 2011-09-11 22:20:48
1315715786 | 2011-09-11 00:36:26
1315622142 | 2011-09-09 22:35:42

Я был бы очень признателен за любую помощь, у меня естьне удалось сделать это с помощью MySQL и никакие онлайн-ресурсы, похоже, не имеют дело с относительной манипулятором датытакой как этот.Мне бы хотелось, чтобы запрос был достаточно модульным, чтобы можно было менять интервал еженедельно, ежемесячно или ежегодно.Заранее спасибо!

Ответ # 1 Ответ:

SELECT
UNIX_TIMESTAMP(created_at) 
AS unix_timestamp,
(
  SELECT MIN(UNIX_TIMESTAMP(created_at))
  FROM my_table
  WHERE created_at >=
    (
    SELECT max(created_at) - 7
    FROM my_table
    )
)
AS `random_1`,
(
  SELECT MIN(UNIX_TIMESTAMP(created_at))
  FROM my_table
  WHERE created_at >=
    (
    SELECT MAX(created_at) - 14
    FROM my_table
    )
)
AS `random_2`
FROM my_table
WHERE created_at =
(
SELECT MAX(created_at)
FROM my_table
)

Возвращает:

unix_timestamp | random_1 | random_2
1317183002 | 1317183002 | 1317183002

Ответ # 2 Ответ:

РЕЗУЛЬТАТ НАСТРОЙКИ:

Это набор результатов для годового интервала:

id  | created_at          | period_index | period_timestamp
267 | 2010-09-27 22:57:05 | 0            | 1317183002
1   | 2009-12-10 15:08:00 | 1            | 1285554786

Мне нужен этот результат:

id  | created_at          | period_index | period_timestamp
626 | 2011-09-28 00:10:02 | 0            | 0
267 | 2010-09-27 22:57:05 | 1            | 1317183002

Надеюсь, это имеет больше смысла.

Ответы [ 2 ]

1 голос
/ 29 сентября 2011

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

Пример 1:

select
  floor(timestampdiff(SECOND, tbl.time, most_recent.time)/604800) as period_index, 
  unix_timestamp(max(tbl.time)) as period_timestamp
from
  tbl
  , (select max(time) as time from tbl) most_recent
group by period_index

дает результаты:

+--------------+------------------+
| period_index | period_timestamp |
+--------------+------------------+
|            0 |       1317183002 | 
|            1 |       1316571974 | 
|            2 |       1315967707 | 
+--------------+------------------+

Это разбивает набор данных на группы на основе «периодов», где (в этом примере) каждый период составляет 7 дней (604800 секунд).period_timestamp, которое возвращается для каждого периода, является «самой последней» (самой последней) отметкой времени, которая попадает в этот период.

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


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


Edit # 1

Вот структура таблицы для приведенного выше примера.

CREATE TABLE `tbl` (
  `id` int(10) unsigned NOT NULL auto_increment PRIMARY KEY,
  `time` datetime NOT NULL
) 

Edit # 2

Хорошо, сначала: я улучшил исходный пример запроса (см. Исправленный «Пример 1» выше).Он по-прежнему работает так же и дает те же результаты, но он чище, эффективнее и проще для понимания.

Теперь ... приведенный выше запрос является групповым запросом, то есть он показывает агрегированные результаты.для групп «период», как я описал выше, а не построчно, как «обычный» запрос.При групповом запросе вы ограничены использованием только агрегированных столбцов.Агрегированные столбцы - это те столбцы, которые названы в предложении group by или вычисляются с помощью агрегатной функции, например MAX(time)).Невозможно извлечь значимые значения для неагрегированных столбцов (например, id) из проекции группового запроса.

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

К счастью, эту проблему довольно легко решить.Просто оберните другой запрос вокруг группового запроса, чтобы выбрать интересующую вас строку за строкой ...

Пример 2:

SELECT 
  entries.id, 
  entries.time, 
  periods.idx as period_index, 
  unix_timestamp(periods.time) as period_timestamp
FROM 
  tbl entries
JOIN
  (select
     floor(timestampdiff( SECOND, tbl.time, most_recent.time)/31536000) as idx, 
     max(tbl.time) as time
   from
     tbl
     , (select max(time) as time from tbl) most_recent
   group by idx
  ) periods
ON entries.time = periods.time

Результат:

+-----+---------------------+--------------+------------------+
| id  | time                | period_index | period_timestamp |
+-----+---------------------+--------------+------------------+
| 598 | 2011-09-28 04:10:02 |            0 |       1317183002 | 
| 996 | 2010-09-27 22:57:05 |            1 |       1285628225 | 
+-----+---------------------+--------------+------------------+

Примечания:

  • В примере 2 используется длина периода 31536000 seconds (365 дней).В то время как в Примере 1 (выше) используется период 604800 seconds (7 дней).Кроме этого, внутренний запрос в Примере 2 такой же, как и основной запрос, показанный в Примере 1.

  • Если совпадающий период_трица принадлежит более чем одной записи (т. Е. Двум или более записямимеют точно такое же время, и это время соответствует одному из выбранных значений period_time), тогда вышеупомянутый запрос (Пример 2) будет включать несколько строк для данной временной метки периода (по одной для каждого соответствия).Какой бы код ни потреблял этот набор результатов, он должен быть подготовлен для обработки такого крайнего случая.

  • Стоит также отметить, что эти запросы будут работать намного, намного лучше, если вы определите индекс для столбца datetime.,Для моего примера схемы это будет выглядеть так:

    ALTER TABLE tbl ADD INDEX idx_time ( time )

0 голосов
/ 28 сентября 2011

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

select unix_timestamp
     , ( select min(unix_tstamp)
           from my_table
          where sql_tstamp >= ( select max(sql_tstamp) - 7
                                  from my_table )
                ) 
    , ( select min(unix_tstamp)
           from my_table
          where sql_tstamp >= ( select max(sql_tstamp) - 14
                                  from my_table )
                )
  from my_table
 where sql_tstamp = ( select max(sql_tstamp)
                        from my_table )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...