提问人:Charles Faiga 提问时间:9/19/2008 最后编辑:fancyPantsCharles Faiga 更新时间:12/29/2020 访问量:76119
如何在MySql中DATETIME字段的日期部分创建索引
How does one create an index on the date part of DATETIME field in 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 用于在交易发生时保存交易的日期和时间
My Table 中有超过 1,000,000 条记录和语句
SELECT * FROM transactionlist where date(TranDateTime) = '2008-08-17'
需要很长时间。
编辑:
看看这篇关于“为什么MySQL的DATETIME可以而且应该避免”的博客文章"
答:
“解释”说了什么?(运行 EXPLAIN SELECT * FROM transactionlist where date(TranDateTime) = '2008-08-17')
如果它因为 date() 函数而没有使用你的索引,则范围查询应该快速运行:
SELECT * FROM transactionlist 其中 TranDateTime >= '2008-08-17' AND TranDateTime < '2008-08-18'
评论
我不知道mySql的具体情况,但是仅对日期字段进行完整索引有什么害处?
然后只需搜索:
select * from translist
where TranDateTime > '2008-08-16 23:59:59'
and TranDateTime < '2008-08-18 00:00:00'
如果索引是 b 树或其他合理的东西,应该很快就会找到它们。
评论
>= '2008-08-16' and ... < '2008-08-18'
00:00:00
如果我没记错的话,这将运行整个表扫描,因为您正在通过函数传递列。MySQL将乖乖地为每一列运行该函数,绕过索引,因为查询优化器无法真正知道该函数的结果。
我会做的是:
SELECT * FROM transactionlist
WHERE TranDateTime BETWEEN '2008-08-17' AND '2008-08-17 23:59:59.999999';
这应该给你2008-08-17发生的一切。
评论
DATETIME
TranDateTime=2008-08-18 00:00:00
BETWEEN
where TranDateTime >= '2008-08-17' and TranDateTime < '2008-08-18'
我并不是说听起来很可爱,但一个简单的方法是添加一个新列,该列仅包含日期部分和索引。
评论
与其基于函数创建索引(如果在 mysql 中可能的话),不如让 where 子句进行范围比较。像这样:
其中 TranDateTime > '2008-08-17 00:00:00' 和 TranDateTime < '2008-08-17 11:59:59')
这允许数据库使用 TranDateTime 上的索引(有一个,对吧?)来执行选择。
瓦列里·克拉夫丘克(Valeriy Kravchuk)在MySQL网站上针对此问题的功能请求中说使用此方法。
“同时,您可以使用字符列将 DATETIME 值存储为字符串,仅对前 N 个字符进行索引。通过在 MySQL 5 中谨慎使用触发器,您可以基于这个想法创建一个相当健壮的解决方案。
您可以编写一个非常容易的例程来添加此列,然后使用触发器使此列保持同步。此字符串列的索引应该非常快。
不能只在日期部分创建索引。你有什么理由吗?
即使您可以只在日期部分上创建索引,优化器可能仍然不会将其用于上述查询。
我想你会发现
SELECT * FROM transactionlist WHERE TranDateTime BETWEEN '2008-08-17' AND '2008-08-18'
高效,做你想做的事。
我不知道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
创建一个仅包含日期的新字段,然后为其编制索引。convert(datetime, left(date_field,10))
评论
date(date_field)
一个非常好的解决方案是使用时间戳作为时间,而不是日期时间。 它被存储为 INT 并被索引得足够好。 就我个人而言,我在事务表上遇到了这样的问题,它有大约一百万条记录并且速度变慢了,最后我指出这是由错误的索引字段(日期时间)引起的。 现在它运行得非常快。
datetime LIKE something% 也不会捕获索引。
使用这个: WHERE datetime_field >= curdate();
这将捕获索引,
并涵盖今天:00:00:00 直到今天:23:59:59
完成。
另一个选项(与版本 5.7.3 及更高版本相关)是基于 datetime 列创建生成的/虚拟列,然后对其进行索引。
CREATE TABLE `table` (
`my_datetime` datetime NOT NULL,
`my_date` varchar(12) GENERATED ALWAYS AS (DATE(`my_datetime`)) STORED,
KEY `my_idx` (`my_date`)
) ENGINE=InnoDB;
评论
如果可以选择修改表,或者您正在编写一个新表,请考虑将日期和时间存储在具有相应类型的单独列中。通过具有更小的键空间和更少的存储空间(与从日期时间派生的仅日期列相比),可以提高性能。这也使得在复合键中使用是可行的,甚至在其他列之前。
在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 | |
+-------------------+------------------+------+-----+---------+----------------+
评论