【问题标题】:mysql: RIGHT JOIN query speed issues involving calendar tablemysql:涉及日历表的RIGHT JOIN查询速度问题
【发布时间】:2012-05-07 05:57:51
【问题描述】:

更新:似乎问题(正如许多人所说)是将日期时间字段更改为查询中的日期字段。

使用DATE( all_griefs_tbl.actioned_date太慢了,有没有更快的方法,既不将actioned_date改成日期字段也不拆分成日期和时间字段?

我有 2 个表,一个包含大量记录,其中包含状态和日期时间字段,另一个是日期从 2008 年到 2015 年的日历表。

我想得到的是一个时间段内的每个日期以及每天“接受”的记录数——即使该计数为零——看起来像这样:

| Date      | number_accepted |
 ----------------------------
 2012-03-01     723
 2012-03-02     723
 2012-03-03     1055
 2012-03-04     1069
 2012-03-05     0
 2012-03-06     615
 2012-03-07     0
 2012-03-08     1072
 2012-03-09     664
 2012-03-10     859
 2012-03-11     0
 2012-03-12     778
 2012-03-13     987

我尝试了以下方法,但它仅在少量数据样本(-1000 行)上足够快。我需要在至少 600k 行上运行良好的东西

SELECT calendar.datefield AS Date, 
       COUNT( all_griefs_tbl.actioned_status ) AS total_griefs
FROM all_griefs_tbl
RIGHT JOIN calendar 
   ON ( DATE( all_griefs_tbl.actioned_date ) = calendar.datefield )
   AND all_griefs_tbl.actioned_status = 'accepted'
WHERE calendar.datefield < CURDATE( )
GROUP BY calendar.datefield

谢谢

编辑:按要求执行计划

 id select_type     table           type    possible_keys     key               key_len     ref     rows    Extra
 1  SIMPLE          calendar        range   PRIMARY           PRIMARY           3           NULL    1576    Using where; Using index
 1  SIMPLE          all_griefs_tbl  ref     actioned_status   actioned_status   153         const   294975  

【问题讨论】:

  • 请提供执行计划(select前加explain时的输出)
  • 每个日期的日历表。从 2008 年到现在,您是否总是每天都去?使用包含每天计数的汇总表可能会更好,而不是每次都重新计算。
  • 这个想法是获取高库存图表的数据 - highcharts.com 所以从 2008 年到现在将是理想的。只要至少有 1 个,我就可以每天获得计数……我想如果我不能让它工作,那将是后备。

标签: mysql database-design join


【解决方案1】:

一些想法......

首先,尽管您声明希望在 db 查询中没有返回任何值的天数,但实际上我会在处理结果集的任何地方进行此检查。每当您进行连接时,您的查询都会变得更加复杂,并且需要更多的内存来处理它们。在这种情况下,我不认为您使用日历表是对关系数据库的特别好的使用。

编辑:澄清一下,查询是如何被调用的?即是否有一些程序(您正在开发)访问数据库、运行查询并显示结果?如果是这样,我建议让这个程序在演示之前处理结果。

其次,如果您致力于“加入”,那么您确实应该在all_griefs_tbl.actioned_date 上有一个索引,因为这是您进行加入的列。或者,您可以在 calendar.datefield 上指定一个外键。

三、需要用到DATE(all_griefs_tbl.actioned_date)这个函数吗?这不是已经约会了吗? (不确定您的数据类型,但如果 this 和 calendar.datefield 不是同一数据类型,这看起来像是糟糕的数据库设计。)

编辑:根据您所说的,您可能希望将all_griefs_tbl.actioned_date 拆分为两列:日期列all_griefs_tbl.actioned_date 和时间戳列all_griefs_tbl.actioned_time。目前,您正在all_griefs_tbl 中的每一行上运行此DATE() 函数以进行连接——这将很快使查询变得迟缓。这也将允许您在 datetime 列上添加索引,这也将提高连接的性能(鉴于您当前的数据库设计,我不是让actioned_date 上的索引感到惊讶并没有帮助 - 我宁愿期待,因为 DATE() 函数,如果您重新运行 EXPLAIN 并在当前的 actioned_date 列上使用索引,它不会t 在all_griefs_tbl 上使用此索引显示它。)

第四,您可能需要考虑all_griefs_tbl.actioned_status 中存储了哪些类型的信息。这可以用布尔值代替吗?这将更有效地存储和处理数据。 (不过,这取决于您的数据库设计。)

编辑:您可以考虑将 all_griefs_tbl.action_status 更改为更小的数据类型 - 我希望它当前是 varchar,但您可以轻松地将其更改为单个(或小)char 数据类型,甚至可以更改为多个布尔值。但是,我不认为这会成为主要的性能开销,实际上是一个更复杂的数据库设计决策,具体取决于您的项目需求。

【讨论】:

  • 感谢您的回复。我正在使用日历表,因此我可以获取某个时间段内的所有日期,以免丢失任何日期。如果有更好的方法,我很想听听。我不致力于加入,我只是不知道另一种方法。我确实有一个关于 all_griefs_tbl.actioned_date 的索引,但它似乎并没有太大的区别。 calendar.datefield 是日期字段,all_griefs_tbl.actioned_date 是日期时间字段。这是唯一的区别。 all_griefs_tbl.actioned_status 可能有几种不同的状态:接受、排队、锁定
  • 我正在努力实现这里所做的事情:richnetapps.com/… 不同之处在于我正在计算时间范围内的行数,而不是汇总值。
  • 使用适当的索引将日期时间拆分为日期和时间就足够了。其他想法还可以,但从性能的角度来看并不那么重要。而且我认为使用日历表并没有那么糟糕。
  • 不确定我做了什么,但再次测试了删除 DATE() 并且它运行了 4.77 秒,这很好。谢谢。
【解决方案2】:

我建议将您的 actioned_date 从日期时间拆分为 2 个单独的日期和时间列,例如 actioned_dateactioned_time,这样您就可以更改您的第一个加入条件

ON ( DATE( all_griefs_tbl.actioned_date ) = calendar.datefield )

ON ( all_griefs_tbl.actioned_date = calendar.datefield )

并添加索引

ALTER TABLE all_griefs_tbl ADD INDEX g_status_date( actioned_status, actioned_date, actioned_time );

它可能会使您对具有 600k 行的表立即进行查询。

【讨论】:

  • 这是我认为可行的一种解决方案,但我想知道是否有另一种方法可以代替 DATE() 可以更快地使用?将其拆分为 2 个单独的列将是最后的手段。
  • 拆分将允许对 group by 使用提到的索引,这将使谈论毫秒而不是秒有所不同。
猜你喜欢
  • 2023-04-01
  • 1970-01-01
  • 2019-08-30
  • 2016-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多