MySQL: правильный способ расчета MTD - PullRequest
0 голосов
/ 22 февраля 2019

Я продолжаю видеть ответы, подобные этому: https://stackoverflow.com/a/21277074/4236404 является принятым по умолчанию ответом о том, как получить строки «Месяц к дате» в запросе MySQL.

Текущие ответы:

SELECT id, created
  FROM table1
 WHERE MONTH(created) = MONTH(CURDATE())
   AND YEAR(created) = YEAR(CURDATE())

Этот запрос будет работать, но он неэффективен, и MySQL выполнит полное сканирование для получения результатов.

Поэтому правильный способ получения строк для запроса на месяц (MTD)выглядит следующим образом (я буду использовать PDO и PHP для демонстрации:

//get the first day of the month
$month_first_day = date( "Y-m-01" );

//select the record where the created date is equal to or bigger than the first day of the current month.
$stmt = $pdo->prepare('SELECT id, created FROM table1 WHERE created >= :first_day_of_month');
$stmt->bindParam(':first_day_of_month', $month_first_day);
$stmt->execute();
$realm_data = $stmt->fetch();

Бонусные запросы:

Чтобы получить все записи за сегодня, сделайте следующее:

$today = date( "Y-m-d" );
$stmt = $pdo->prepare('SELECT id, created FROM table1 WHERE created >= :today');
$stmt->bindParam(':today', $today);
$stmt->execute();
$realm_data = $stmt->fetch();

Чтобы получить все записи за вчерашний день, выполните следующие действия:

$today = date( "Y-m-d" );
$yesterday = date( "Y-m-d", time()-86400 );
$stmt = $pdo->prepare('SELECT id, created FROM table1 WHERE created >= :yesterday AND created < :today');
$stmt->bindParam(':today', $today);
$stmt->bindParam(':yesterday', $yesterday);
$stmt->execute();
$realm_data = $stmt->fetch();

Чтобы получить все записи за последние 7 дней, выполните следующие действия:

$seven_days_ago = date('Y-m-d', strtotime('-7 days'));
$stmt = $pdo->prepare('SELECT id, created FROM table1 WHERE created >= :seven_days_ago');
$stmt->bindParam(':seven_days_ago', $seven_days_ago);
$stmt->execute();
$realm_data = $stmt->fetch();

Допущения / подсказка:

Столбец в MySQL имеет формат даты и времени.

1 Ответ

0 голосов
/ 23 февраля 2019

MySQL не позволяет создавать индексы на основе выражений / функций, существует обходной путь, который возможен из MySQL 5.7+ с сгенерированными столбцами , которые можно индексировать.Обязательно создайте таблицы как InnoDB, которые могут создавать индексы для несохраненных сгенерированных столбцов.

CREATE TABLE t (
 d DATETIME 
 , dy INT AS (YEAR(d)) 
 , dm INT AS (MONTH(d))
 , INDEX(dy, dm)                    
) Engine = InnoDB ; 

Объясните

EXPLAIN
SELECT 
 *
FROM 
 t
WHERE
    MONTH(d) = MONTH(CURDATE())
  AND
    YEAR(d) = YEAR(CURDATE())

Результат

| id  | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra       |
| --- | ----------- | ----- | ---------- | ---- | ------------- | --- | ------- | --- | ---- | -------- | ----------- |
| 1   | SIMPLE      | t     |            | ALL  | dy            |     |         |     | 19   | 78.95    | Using where |

см. демо

Да, я знаю, что написано ALL, но MySQL прямо здесь.В результате возвращено 15 записей, в таблице 19 записей.

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

Но в 15 раз произвольный дисковый ввод-вывод (6 мс * 15) и потоковая передача (много) обходятся дороже, чем полное сканирование таблицы, которое выполняет один случайный дисковый ввод-вывод (6 мс * 1) и может передавать данные за одинидти.

Оптимизатор / выполнение MySQL основан на затратах, которые вычисляют относительные затраты на основе случайных дисковых операций ввода-вывода, которые являются самыми дорогими.

Но обратите внимание, что он сказал возможный ключ dy MySQL знает, что он может использоватьиндекс теперь для

WHERE MONTH(d) = MONTH(CURDATE()) AND YEAR(d) = YEAR(CURDATE())

Только MySQL думал, что я не собираюсь делать больше работы, читая индекс.

Все еще жду, пока MySQL поддержит выражения / функциональные индексывозможно они добавляют позже версию MySQL 8.MySQL 8 все еще работает и получает обновления.

CREATE INDEX index_name ON table (YEAR(d));

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

MySQL до MySQL 8.0.16 также не поддерживал ограничения CHECK, они также не спешили его реализовывать, потому что MySQL уже поддерживал его более или менее с VIEW с CHECKВАРИАНТ , чтобы привести хороший пример.

Редактировать:

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

я заметил MySQL 8.0.13 обновить добавленные выражения / функциональные индексы.

Функциональные ключевые части

«Нормальный» индекс индексирует значения столбцов или префиксы значений столбцов.Например, в следующей таблице запись индекса для данной строки t1 включает полное значение col1 и префикс значения col2, состоящий из его первых 10 символов:

MySQL 8.0.13 и выше поддерживает функциональный ключчасти, которые индексируют значения выражения, а не значения префикса столбца или столбца.Использование функциональных ключевых частей позволяет индексировать значения, которые не хранятся непосредственно в таблице.Примеры:

CREATE TABLE t1 (col1 INT, col2 INT, INDEX func_index ((ABS(col1)))); CREATE INDEX idx1 ON t1 ((col1 + col2)); CREATE INDEX idx2 ON t1 ((col1 + col2), (col1 - col2), col1); ALTER TABLE t1 ADD INDEX ((col1 * 40) DESC);

см. source

В MySQL 8.0.13+ вы можете сделать

CREATE INDEX index_name ON table (YEAR(created), MONTH(created));

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