如何在MySql中的DATETIME字段的日期部分创建索引

时间:2008-09-18 18:15:25

标签: 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' 

需要很长时间。

编辑:

请查看有关“Why MySQL’s DATETIME can and should be avoided

的博客文章

13 个答案:

答案 0 :(得分:61)

如果我没记错的话,那将会运行整个表格扫描,因为你正在通过一个函数传递列。 MySQL将乖乖地为每一列运行该函数,绕过索引,因为查询优化器无法真正知道函数的结果。

我会做的是:

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

那应该会给你2008-08-17发生的一切。

答案 1 :(得分:10)

我并不是说听起来很可爱,但一个简单的方法是添加一个只包含日期部分和索引的新列。

答案 2 :(得分:9)

您无法仅在日期部分创建索引。你有什么理由吗?

即使您可以仅在日期部分创建索引,优化器也可能仍然不会将其用于上述查询。

我想你会找到那个

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

效率高,能做你想做的事。

答案 3 :(得分:7)

另一个选项(relevant for version 5.7.3 and above)是基于日期时间列创建生成/虚拟列,然后将其编入索引。

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;

答案 4 :(得分:3)

我不知道mySql的具体细节,但只是完整地索引日期字段有什么害处?

然后只搜索:

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

如果索引是b-trees或其他合理的东西,这些应该很快找到。

答案 5 :(得分:2)

Valeriy Kravchuk就MySQL网站上这个问题的功能请求说使用这种方法。

“与此同时,您可以使用字符列将DATETIME值存储为字符串,只有前N个字符被编入索引。在MySQL 5中仔细使用触发器,您可以根据这个想法创建一个相当强大的解决方案。”< / p>

您可以编写一个非常容易添加此列的例程,然后使用触发器保持此列同步。此字符串列的索引应该非常快。

答案 6 :(得分:2)

一个很好的解决方案是使用时间戳作为时间而不是日期时间。 它存储为INT并且索引足够好。 我个人在交易表上遇到过这样的问题,即有大约百万条记录并且速度慢下来,最后我指出这是由不良索引字段(datetime)引起的。 现在它运行得非常快。

答案 7 :(得分:1)

我不知道mySQL的具体细节,但只是完整地索引日期字段有什么危害呢?

如果你使用*树的功能魔法,哈希,......就会消失,因为要获得值,你必须调用该函数。但是,因为你不知道未来的结果,你必须对表格进行全面扫描。

没有什么可以补充的。

也许你的意思是计算(计算?)索引...但到目前为止,我只在IntersystemsCaché中看到过这个。我不认为关系数据库(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.000000:00没有任何区别(我通常以此格式使用它)。

答案 8 :(得分:1)

datetime LIKE some%也不会捕获索引。

使用此: WHERE datetime_field&gt; = curdate();
这将抓住指数,
今日:00:00:00至今日:23:59:59
完成。

答案 9 :(得分:0)

'解释'说什么? (运行EXPLAIN SELECT * FROM transactionlist where date(TranDateTime)='2008-08-17')

如果因为date()函数而没有使用索引,则范围查询应该快速运行:

SELECT * FROM transactionlist,其中TranDateTime&gt; ='2008-08-17'AND TranDateTime&lt; '2008-08-18'

答案 10 :(得分:0)

不是基于函数创建索引(如果在mysql中甚至可能),请使where子句进行范围比较。类似的东西:

  

TranDateTime&gt; “2008-08-17   00:00:00'和TranDateTime&lt;   '2008-08-17 11:59:59')

这让数据库使用TranDateTime上的索引(有一个,对吗?)来进行选择。

答案 11 :(得分:0)

如果修改表是一种选择,或者您要编写一个新表,请考虑将日期和时间存储在具有相应类型的单独列中。通过减小键空间并减少存储量(与从datetime导出的仅日期的列相比),可以提高性能。这也使得甚至在其他列之前也可以在复合键中使用它。

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

答案 12 :(得分:-1)

创建一个只包含日期convert(datetime, left(date_field,10))的新字段,然后将其编入索引。