Как я могу улучшить свой оператор SQL с еженедельными результатами с неделей, начинающейся в четверг или любой другой день недели?

8

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

Мне нужно получить данные за данную неделю (отчетные недели начинаются в четверг). Я написал следующий запрос:

   SELECT * 
FROM `table` 
WHERE 1 = CASE 
  WHEN WEEKDAY(NOW()) = 0 THEN DATEDIFF(NOW(),`date`) BETWEEN -2 AND 4
  WHEN WEEKDAY(NOW()) = 1 THEN DATEDIFF(NOW(),`date`) BETWEEN -1 AND 5
  WHEN WEEKDAY(NOW()) = 2 THEN DATEDIFF(NOW(),`date`) BETWEEN -0 AND 6
  WHEN WEEKDAY(NOW()) = 3 THEN DATEDIFF(NOW(),`date`) BETWEEN -6 AND 0
  WHEN WEEKDAY(NOW()) = 4 THEN DATEDIFF(NOW(),`date`) BETWEEN -5 AND 1
  WHEN WEEKDAY(NOW()) = 5 THEN DATEDIFF(NOW(),`date`) BETWEEN -4 AND 2
  WHEN WEEKDAY(NOW()) = 6 THEN DATEDIFF(NOW(),`date`) BETWEEN -3 AND 3
END

Похоже, это работает на начальном тестировании. Но я не уверен, что это лучший способ сделать это. Я не знаю много о производительности MySQL, но там будет более ста тысяч записей для фильтрации. Будет ли этот запрос очень медленным из-за количества проверенных условий?

Функция NOW () используется при извлечении самого последнего отчета - однако в некоторых случаях мне нужно будет делать отчеты за другие недели - поэтому я бы подставил другую дату на место.

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

Я не могу использовать функцию WEEK (), потому что вы можете начать с нее только неделю или воскресенье.

Любые идеи по улучшению этого запроса очень ценятся!

Другие примечания: В настоящее время используется MariaDB 5.3.

Джеймс Ли Ванн
источник

Ответы:

5

Вот запрос, который я написал, чтобы дать вам самый последний четверг и конец среды

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

Вот пример на сегодня 2011-09-21

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2011-09-15 00:00:00 | 2011-09-21 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

Просто замените вызовы функций NOW () на любое время и дату, и у вас будет неделя, начинающаяся с четверга, все время для выбранной вами даты и времени.

Вот еще один пример с использованием конкретной даты «2011-01-01»

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE('2011-01-01') - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE('2011-01-01') dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2010-12-30 00:00:00 | 2011-01-05 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

Ваш запрос tableссылки сегодня будет выглядеть примерно так:

SELECT * from `table`,
(SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A) M
WHERE `date` >= thu_beg
AND `date` <= wed_end;

Попробуйте!

ОБНОВЛЕНИЕ 2011-09-22 16:27 ПО ВОСТОЧНОМУ ВРЕМЕНИ

Это был мой предложенный запрос для маркировки чт-ср.

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

Как насчет других недель ???

  • (SELECT SUBSTR('6012345',wkndx,1) в неделю с понедельника по воскресенье
  • (SELECT SUBSTR('5601234',wkndx,1) в неделю с вторника по понедельник
  • (SELECT SUBSTR('4560123',wkndx,1) делает неделю, начиная с Ср, заканчивая вт
  • (SELECT SUBSTR('3456012',wkndx,1) Неделя, начинающаяся с четверга, заканчивается
  • (SELECT SUBSTR('2345601',wkndx,1) в неделю с пятницы по четверг
  • (SELECT SUBSTR('1234560',wkndx,1) с недели по субботу?
  • (SELECT SUBSTR('0123456',wkndx,1) в неделю, начиная с Солнца, заканчивая сб
RolandoMySQLDBA
источник
1

Позвольте мне повторить то, что вы просите, чтобы убедиться, что я правильно понял:

Вы хотите вытащить все записи за последние 7 дней к указанной дате?

Это может выглядеть так:

select * from table where `date` between $date - interval 7 day and $date

Важно отметить, что $ date - это не буквальный синтаксис mysql, а просто пример заполнителя для желаемой даты начала. Если это для отчетности, я думаю, что запрос будет в конечном итоге сгенерирован из какого-то сценария? Если это так, в этом языке это может быть проще, а затем передавать литеральное значение как часть составленного запроса.

Я фанат того, чтобы запросы были как можно более простыми, поэтому я бы так и оставил. Я оставлю место для других, чтобы они могли дать ответы, чтобы выполнить то, что вы хотите, в одном запросе SQL-fu.

Изменить: После перечитывания вашего поста кажется, что вы, вероятно, используете тип даты. В этом случае следующий блок курсивом может быть избыточным. Я оставляю это в случае, если это полезно для других (и так как я нашел время, чтобы написать это :-)

Вы сказали, что используете "метку даты"? Технически это не тип данных Mysql. Это дата, время или дата (время и год также существуют, но, по вашему мнению, они не применимы)? Я спрашиваю, потому что вы можете сделать это просто столбцом даты вместо других. Если это правильный выбор для вас, на самом деле зависит от того, как столбец используется и таблица запрашивается по всем. Если его единственная цель - просто извлекать записи в диапазоне дат независимо от времени, тогда дата определенно является подходящим способом. Для одного потребовалось только 3 байта вместо 4 или 8. Я могу уточнить другие причины, по которым вы хотите использовать дату, если она соответствует вашим требованиям использования (т. Е. Вас не волнует часть времени). Вы можете найти подробную информацию о различных типах на http://dev.mysql.com/doc/refman/5.0/en/datetime.html

http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html

Если и только если вы используете дату конкретно, вы можете рассмотреть следующее:

Запустите запрос с префиксом «объяснение». Подробную информацию о том, как интерпретировать результаты и что лучше всего, можно найти по адресу http://dev.mysql.com/doc/refman/5.0/en/explain-output.html.

Как только вы это объясните, измените запрос на

select * from table where date in ("N", "N+1"..."N+7")

где вы перечисляете все отдельные даты, которые вас интересуют. Я сталкивался с ситуациями, когда ясно, что MySQL недостаточно умен, чтобы эффективно использовать строки запроса диапазона (т. е. между x и y), перечисляющие конкретные значения для небольших наборов.

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

atxdba
источник
1

Мистер @Rolando, очевидно, отвечает на вопрос, но я предложу другое решение, которое позволит вам манипулировать и настраивать календари между часовыми поясами и самой важной группой по неделям, которые определены в разных часовых поясах.

Итак, давайте предположим, что ваш MySQL работает на сервере, настроенном по UTC, и вы хотите иметь собственный календарь, который будет на 7 часов вперед, и поэтому ваши недели должны начаться в субботу 7 часов утра.

CREATE TABLE `wh_blur_calendar` (
  `date` timestamp NOT NULL ,
  `y` smallint(6) DEFAULT NULL,
  `q` tinyint(4) DEFAULT NULL,
  `m` tinyint(4) DEFAULT NULL,
  `d` tinyint(4) DEFAULT NULL,
  `w` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `wh_ints` (
  `i` tinyint(4) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into wh_ints values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

Теперь популярное декартово соединение, которое должно заполнить ваш стол:

INSERT INTO wh_blur_calendar (date)
SELECT DATE('2010-01-01 00:00:00 ') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
FROM wh_ints a JOIN wh_ints b JOIN wh_ints c JOIN wh_ints d JOIN wh_ints e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) < 10245
ORDER BY 1;

Давайте обновим часы:

update  db_wh_repo.wh_blur_calendar set date = date_add(date, interval 7 hour);

и, наконец, организовать неделю вашего календаря в обычном порядке

UPDATE wh_blur_calendar
SET 
    y = YEAR(date),
    q = quarter(date),
    m = MONTH(date),
    d = dayofmonth(date),
    w = week(date_add((date), interval 1 day));

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

ninjabber
источник