Как создать индекс для части даты поля DATETIME в MySql - PullRequest
64 голосов
/ 18 сентября 2008

Как мне создать индекс для части даты поля DATETIME?

mysql> SHOW COLUMNS FROM transactionlist;
+-------------------+------------------+------+-----+---------+----------------+
| Field             | Type             | Null | Key | Default | Extra          |
+-------------------+------------------+------+-----+---------+----------------+
| TransactionNumber | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| WagerId           | int(11)          | YES  | MUL | 0       |                |
| TranNum           | int(11)          | YES  | MUL | 0       |                |
| TranDateTime      | datetime         | NO   |     | NULL    |                |
| Amount            | double           | YES  |     | 0       |                |
| Action            | smallint(6)      | YES  |     | 0       |                |
| Uid               | int(11)          | YES  |     | 1       |                |
| AuthId            | int(11)          | YES  |     | 1       |                |
+-------------------+------------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)

TranDateTime используется для сохранения даты и времени транзакции, как это происходит

Моя таблица содержит более 1 000 000 записей и утверждение

SELECT * FROM transactionlist where date(TranDateTime) = '2008-08-17' 

занимает много времени.

РЕДАКТИРОВАТЬ:

Посмотрите на этот пост в блоге " Почему DATETIME в MySQL можно и нужно избегать "

Ответы [ 13 ]

61 голосов
/ 18 сентября 2008

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

Что бы я сделал, это что-то вроде:

SELECT * FROM transactionlist 
WHERE TranDateTime BETWEEN '2008-08-17' AND '2008-08-17 23:59:59.999999';

Это должно дать вам все, что произошло 2008-08-17.

10 голосов
/ 18 сентября 2008

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

9 голосов
/ 18 сентября 2008

Вы не можете создать индекс только для части даты. Есть ли причина, по которой вы должны это сделать?

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

Я думаю, вы найдете это

SELECT * FROM transactionlist WHERE TranDateTime BETWEEN '2008-08-17' AND '2008-08-18'

Эффективен и делает то, что вы хотите.

7 голосов
/ 14 июня 2017

Другой вариант (, относящийся к версии 5.7.3 и выше ) - создать сгенерированный / виртуальный столбец на основе столбца datetime, а затем проиндексировать его.

CREATE TABLE `table` (
`my_datetime` datetime NOT NULL,
`my_date` varchar(12) GENERATED ALWAYS AS (DATE(`my_daetime`)) STORED,
KEY `my_idx` (`my_date`)
) ENGINE=InnoDB;
3 голосов
/ 18 сентября 2008

Я не знаю о специфике mySql, но какой вред в том, чтобы индексировать поле даты целиком?

Тогда просто поиск:

 select * from translist 
     where TranDateTime > '2008-08-16 23:59:59'
        and TranDateTime < '2008-08-18 00:00:00'

Если индексы представляют собой b-деревья или что-то еще, что разумно, они должны быть найдены быстро.

2 голосов
/ 27 февраля 2012

Единственное и хорошее решение, которое довольно хорошо работает, - это использовать временную метку как время, а не как время. Он хранится как INT и достаточно хорошо индексируется. Лично я столкнулся с такой проблемой в таблице транзакций, которая имеет около миллиона записей и сильно замедлилась, наконец, я указал, что это вызвано неправильным индексированным полем (datetime). Теперь он работает очень быстро.

2 голосов
/ 18 сентября 2008

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

"Тем временем вы можете использовать символьные столбцы для хранения значений DATETIME в виде строк, причем индексируются только первые N символов. При некотором осторожном использовании триггеров в MySQL 5 вы можете создать достаточно надежное решение, основанное на этой идее."

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

1 голос
/ 17 октября 2014

datetime LIKE что-то% тоже не поймает индекс.

Используйте это: ГДЕ datetime_field> = curdate ();
Это поймает индекс,
и обложка сегодня: 00: 00: 00 и сегодня: 23: 59: 59
Готово.

1 голос
/ 11 марта 2010

Я не знаю о специфике mySQL, но какой вред в том, чтобы индексировать поле даты целиком?

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

Добавить нечего.

Может быть, вы имеете в виду что-то вроде вычисляемых (вычисляемых?) Индексов ... но на сегодняшний день я видел это только в Intersystems Caché. Я не думаю, что есть случай в реляционных базах данных (AFAIK).

Хорошим решением, на мой взгляд, является следующее (обновленный пример clintp):

SELECT * FROM translist 
WHERE TranDateTime >= '2008-08-17 00:00:00.0000'
  AND TranDateTime < '2008-08-18 00:00:00.0000'

На мой взгляд, не имеет значения, используете ли вы 00:00:00.0000 или 00:00 (я обычно использовал его в этом формате).

0 голосов
/ 14 декабря 2018

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

В случае ОП:

+-------------------+------------------+------+-----+---------+----------------+
| Field             | Type             | Null | Key | Default | Extra          |
+-------------------+------------------+------+-----+---------+----------------+
| TransactionNumber | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| WagerId           | int(11)          | YES  | MUL | 0       |                |
| TranNum           | int(11)          | YES  | MUL | 0       |                |
| TranDate          | date             | NO   |     | NULL    |                |
| TranTime          | time             | NO   |     | NULL    |                |
| Amount            | double           | YES  |     | 0       |                |
| Action            | smallint(6)      | YES  |     | 0       |                |
| Uid               | int(11)          | YES  |     | 1       |                |
| AuthId            | int(11)          | YES  |     | 1       |                |
+-------------------+------------------+------+-----+---------+----------------+
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...