0

MySQL 的内置缓存确实让这个问题在一天的大部分时间里都没有实际意义,但是第一次运行以下查询时,性能很糟糕:第一次需要超过 300 秒,而后续查询可以在几毫秒内完成。使用 SQL_NO_CACHE 运行它需要 2-4 秒(!),这在这种情况下是可以接受的——但初始运行时间不是。

SELECT DATEDIFF( bt.`datetime`, st.`datetime`) AS 'day_separation'
FROM `smallerTable` AS st
LEFT OUTER JOIN `bigTable` AS bt ON bt.item_id = st.item_id
  AND bt.code = 'X'
  AND bt.`datetime` > st.`datetime`
  AND DATEDIFF ( bt.datetime, st.datetime) < 11
  AND st.`datetime` > '2012-07-01' AND st.`datetime` < 'yesterdays-date 23:59:59'

我已经引入了多列索引(感谢这个问题),但它仍然无法解决这个特定问题。这个解决方案看起来很有灵感,但我认为它不适用,因为我不确定如何合并这些结果。

较小的表有约 8000 条记录,我现在想计算/包括所有记录。它最终会变大并包含 2012-07-01 之前的项目。

bigTable 有 1000 万条记录,我只想将这些记录的“配对”与较小的表匹配。部分麻烦是他们不能共享直接键或将它们链接在一起的引用,所以我只剩下一个LEFT OUTER JOIN并猜测如果两个事件的时间戳相隔 < 11 天(并共享其他条件),它们必须是有关的。

排除测试DATEDIFF ( bt.datetime, st.datetime) < 11创建了 14k 个“结果”,说明“需要发生”的DATEDIFF计算数量为 14k-8k(又名 6k)。

INDEXESdatetime每个表的字段,thecodeitem_ids。

我在两个表上都有复合索引,顺序为(item_id, datetime). 据我了解,这是必要的顺序,因为我们在 select 语句中以DATEDIFF( bt.datetime, st.datetime).

组合索引会(code, item_id, datetime)彻底改变这个查询吗?(是的,它确实!)

解释对我未经训练的眼睛几乎没有透露,只是它使用了一个临时表,我知道这可能很耗时。

id * select_type * table * type  * possible_keys * key                * key_len * ref           * rows * extra
1  * SIMPLE      * st    * index * NULL          * items_for_datetime * 59      * NULL          * 8295 * using index; using temporary; using filesort
1  * SIMPLE      * BT    * ref   * [many]        * items_for_datetime * 51      * master.st.item_id * 3    *

根据 MySQL 的突发奇想,bigTable有时会显示它更喜欢item_idkey 而不是items_for_datetime. 我应该鼓励使用我的联合指数,相信我知道得更好吗?

一些额外的信息:

  • 每天对这些表进行一次插入(BT 中有 1~5k 条记录)
  • 没有更新或删除发生
  • 我可能会运行两个查询——将这个更改为 INNER JOIN,然后运行第二个查询以从总记录中减去结果数,以找到在 BT 中没有相应结果的数字
  • 我们已经在 BT 上执行了 phpmyadmin 的Check Table, Defragmentation, 和Optimize Table

[旁白] 这可能是使用 NoSQL 数据库(如 Mongo)的好场景吗?

为什么第一次和第二次会有这么大的差距?更重要的是:可以做些什么来改善第一次运行的时机?

更新:新的尝试需要新的一天才能发现它们的功效。BETWEEN明天我将使用and尝试 Barmar 的建议DATE_ADD。我还在(code, item_id, datetime). 我将在明天报告结果,但欢迎任何其他想法。

更新:成功!查询的第一次运行现在只用了 6 秒,考虑到它的来源,这真是太棒了。随后的查询只用了 0.035 秒!多么梦想。毫无疑问,综合指数(code, item_id, datetime)促成了这一成功。这是新的查询:谢谢大家!

SELECT DATEDIFF( bt.`datetime`, st.`datetime` ) AS  'day_separation'
FROM  `smallerTable` AS st
LEFT OUTER JOIN bigTable AS bt USE INDEX (  `cmd_item_time` ) 
ON bt.item_id = st.item_id
  AND bt.code =  'X'
  AND bt.`datetime` BETWEEN st.`datetime` AND DATE_ADD( st.`datetime`, INTERVAL 10 DAY ) 
  AND st.datetime BETWEEN '2012-07-01' AND  'yesterdays-date 23:59:59'
4

2 回答 2

1

尝试改变:

AND bt.`datetime` > st.`datetime`
AND DATEDIFF ( bt.datetime, st.datetime) < 11

至:

AND bt.`datetime` BETWEEN st.`datetime` AND date_add(st.`datetime`, interval 11 day)

这可能允许使用索引bt.datetime

如果code = 'X'过滤掉 的大部分bigTable,一个复合索引(code, item_id)应该会有所帮助。

于 2013-04-15T14:58:43.270 回答
0

您查询的问题很可能是那一行:AND st。datetime> '2012-07-01' 和圣。datetime< '昨天-日期 23:59:59'

通过将日期时间转换为字符串(以便比较),您将失去索引的优势......

于 2013-04-15T14:46:18.250 回答