2008-09-18 2 views
57

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

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 используется для сохранения даты и времени сделки, как это происходит

Моя таблица имеет более 1000000 записей в ней и о

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

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

EDIT:

Взгляните на этом блоге на "Why MySQL’s DATETIME can and should be avoided"

+3

предупреждающий комментарий к ссылке, которую вы предложили посмотреть: сообщение написано с таким волнением и яростью, что оно почти граничит с ребячеством. И писатель не отбрасывает никакой критики, все еще отмечая, что он стоит за тем, что он сказал, но его точка с каждым снижается. Но все же, не пустая трата времени, если вы прочтете комментарии. – kommradHomer 2013-01-24 10:05:47

ответ

50

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

+0

, что действительно очень эффективно ... – Arfeen 2012-07-13 15:40:24

+1

Я привык думать об этом использовании как о простом сокращении для «ГГГГ-ММ-ДД 00:00:00» – kommradHomer 2013-01-24 10:06:53

+3

Я знаю, что это старый ответ, но я чувствую себя вынужденным что MySQL использует сравнение строк для `DATETIME`; ваш запрос возвращает правильные результаты и не включает строки с `TranDateTime = 2008-08-18 00: 00: 00`. – Arth 2014-07-30 09:17:51

0

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

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

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

+1

Если вы используете дату(), вы не попадете в индекс. Mysql не может использовать индексы внутри таких вызовов функций. – JBB 2008-09-18 18:26:13

3

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

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

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

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

9

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

0

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

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

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

2

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

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

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

8

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

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

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

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

Является эффективным и делает то, что вы хотите.

0

Я не знаю о специфике 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

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

1

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

-2

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

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

datetime НРАВИТСЯ, что что-то% не поймает индекс.

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

5

Другой вариант (относится к вер. 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; 
Смежные вопросы