【问题标题】:MySQL fastest way to search by DATE if a record exist如果记录存在,MySQL 按 DATE 搜索的最快方法
【发布时间】:2015-04-15 10:40:01
【问题描述】:

我找到了很多按日期搜索 mysql 记录的方法

方法一:

SELECT id FROM table WHERE datetime LIKE '2015-01-01%' LIMIT 1

方法二(同方法一+ORDER BY):

SELECT id FROM table WHERE datetime LIKE '2015-01-01%' ORDER BY datetime DESC LIMIT 1

方法三:

SELECT id FROM table WHERE datetime BETWEEN '2015-01-01' AND '2015-01-01 23:59:59' LIMIT 1

方法四:

SELECT id FROM table WHERE DATE_FORMAT( datetime, '%y.%m.%d' ) = DATE_FORMAT( '2015-01-01', '%y.%m.%d' )

方法5(我认为是最慢的):

SELECT id FROM table WHERE DATE(`datetime`) = '2015-01-01' LIMIT 1

什么是最快的?

在我的例子中,表有 100 万行,搜索的日期总是最近的。

【问题讨论】:

    标签: mysql date search select optimization


    【解决方案1】:

    你提到的最快的方法是

    SELECT id 
      FROM table 
     WHERE datetime BETWEEN '2015-01-01' AND '2015-01-01 23:59:59'
     LIMIT 1
    

    当您在 datetime 列上创建索引时,这会变得更快。可以随机访问索引以找到第一个匹配行,然后扫描直到最后一个匹配行。所以没有必要读取整个表,甚至整个索引。而且,当您使用LIMIT 1 时,它只会读取单行。速度非常快,即使在一张巨大的桌子上也是如此。

    您的其他搜索方式将函数应用于每一行:

    • datetime LIKE '2011-01-01%'datetime 转换为每一行的字符串。
    • 方法 3,4 和 5 都在每一行的内容上使用显式函数,例如 DATE()

    使用这些函数可以避免使用索引来查找数据。

    专业提示:不要将BETWEEN 用于日期算术,因为它不能很好地处理结束条件。而是使用

     WHERE datetime >= '2015-01-01' 
       AND datetime <  '2015-01-02'
    

    这与BETWEEN 一样好,并且使您不必将2015-01-01 的最后时刻明确写为23:59:59。无论如何,这对于更高精度的时间戳是不正确的。

    【讨论】:

    • 我发现这会导致数据库强制转换列以匹配文字类型(日期),从而避免任何索引。
    • @Bohemian,在 MySQL 5.1+ 中完成了大量此操作,我从未遇到过您提到的问题。 MySQL 的查询计划器尝试将常量转换为与列相同的类型,并且只有在这不起作用时才会将列转换为常量类型。格式正确的日期字符串转换为 DATETIME 就好了。
    • 谢谢!假设我在此表中有旧记录(2010、2011 等)并且我总是搜索最近的日期(如今天、昨天),如果添加 ORDER BY datetime DESC 会更快吗?例如SELECT id FROM table WHERE datetime &gt;= '2015-01-01' AND datetime &lt; '2015-01-02' ORDER BY datetime DESC LIMIT 1
    • 如果您在datetime 上有索引,则使用LIMIT 1 进行范围扫描的速度应该与使用或不使用ORDER BYORDER BY DESC 的速度大致相同。 MySql 没有任何逆序索引功能,但它确实能够在索引查找中找到 last 匹配值以及第一个匹配值。
    • @OllieJones -- 我不敢苟同。 (1) 只有方法 3 针对以 (datetime) 开头的索引进行了优化。 (2) MySQL 确实优化了ORDER BY foo DESC LIMIT 10。您可以通过在SELECT 之前执行FLUSH STATUS; 来查看差异,然后在之后执行SHOW STATUS LIKE 'Handler_read%';。请注意,其中一个值将显示表中的行数(未优化)或接近 LIMIT 值的数字(优化)。
    【解决方案2】:

    假设日期时间列上有索引,最快的方法是方法 3 的变体,除了 两个 范围值都是日期时间文字:

    SELECT id FROM table
    WHERE datetime BETWEEN '2015-01-01 00:00:00' AND '2015-01-01 23:59:59'
    LIMIT 1
    

    使用与列相同类型的字面量意味着不会对列进行任何强制转换来执行比较,从而提供在列上使用索引的最佳机会。我已经在生产中使用它,效果很好。

    【讨论】:

    • 不只是文字。任何“常量表达式”都有效——例如NOW() - INTERVAL 2 HOURCURDATE() - INTERVAL 3 MONTH
    • @rick 当然,只要表达式的类型与列完全匹配。例如,NOW() 返回一个日期戳,CURDATE() 返回一个日期,这需要在某处进行强制转换。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 2016-07-02
    • 2021-08-26
    • 1970-01-01
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多