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

Как создать индекс в части даты поля 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' 

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

ИЗМЕНИТЬ:

Взгляните на это сообщение в блоге " Почему MySQL DATETIME можно и следует избегать"

Ответы

Ответ 1

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

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

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

Это должно дать вам все, что произошло в 2008-08-17 годах, и все, что произошло точно в 2008-08-18 00:00:00. Если это проблема, вы можете изменить второй термин на "2008-08-17 23:59:59" и просто получить 2008-08-17.

Ответ 2

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

Ответ 3

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

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

Я думаю, вы обнаружите, что

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

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

Ответ 4

Другая опция (относящаяся к версии 7.5.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;

Ответ 5

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

Затем просто выполните поиск:

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

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

Я не знаю о специфике 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, по моему мнению, не имеет значения (я обычно использовал его в этом формате).

Ответ 9

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

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

Ответ 10

Что означает "объяснять"? (запустите EXPLAIN SELECT * FROM transactionlist, где дата (TranDateTime) = '2008-08-17')

Если он не использует ваш индекс из-за функции date(), запрос диапазона должен выполняться быстро:

SELECT * FROM transactionlist, где TranDateTime >= '2008-08-17' AND TranDateTime < '2008-08-18'

Ответ 11

Вместо того, чтобы делать индекс, основанный на функции (если это возможно даже в mysql), сделайте предложение where, которое делает сравнение диапазона. Что-то вроде:

Где TranDateTime > '2008-08-17 00:00:00 'и TranDateTime < '2008-08-17 11:59:59')

Это позволяет БД использовать индекс в TranDateTime (есть один, правый?), чтобы сделать выбор.

Ответ 12

Создайте новые поля только с датами convert(datetime, left(date_field,10)), а затем проиндексируйте их.

Ответ 13

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

В случае OP:

+-------------------+------------------+------+-----+---------+----------------+
| 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       |                |
+-------------------+------------------+------+-----+---------+----------------+

Ответ 14

Почему никто не предлагал использовать LIKE? Разве это не делает работу? Будет ли это так быстро, как МЕЖДУ?

SELECT * FROM transactionlist where TranDateTime LIKE '2008-08-17%'