【问题标题】:Specific SQL Query Optimization Help Needed需要特定的 SQL 查询优化帮助
【发布时间】:2011-03-15 10:34:37
【问题描述】:

所以我正在从事一个数据挖掘项目,我们正在研究代码元素及其关系以及随着时间的推移对这些事物的变化。我们想要问一些关于相关元素多久更改一次的问题。我已将其设置为视图,但运行大约需要 10 分钟。我相信问题是我必须做很多减法、连接和字符串比较来比较条目(对于我们的窗口大小),但我不知道解决这个问题的好方法。查询看起来像

select aw.same
     , rw.k
     , count(distint concat_ws(',', r1.id, r2.id)) as num  
  from deltamethoddeclaration dmd1
    join revision r1
      on r1.id=FKrevID 
    join methodinvocation mi
      on mi.FKcallerID = dmd1.FKMDID 
    join deltamethoddeclaration dmd2 
      on mi.FKcalleeID = dmd2.FKMDID
    join revision r2 
      on r2.id = dmd2.FKrevID
    join revisionwindow rw
    join authorwindow aw
  where (dmd1.FKrevID - dmd2.FKrevID) < rw.k
    and (dmd2.FKrevID - dmd1.FKrevID) < rw.k
    and case aw.same
          when 1 then
            r1.author = r2.author
          when 0 then
            r1.author <> r2.author
          else
            1=1
         end
  group by aw.same
         , rw.k
;

好的,revisionwindow 存储了我们感兴趣的修订窗口(10、20、50、100),而 authorwindow 存储了我们想要的作者类型(相同、不同和不关心)。部分问题是,我们可以有相同的修订对与不同的元素匹配,所以我能想出的唯一技巧就是那个丑陋的 count(distinct concat()) 东西。这应该返回一个有 12 行的表,每个组合一个作者和修订窗口。 “num”下的条目是以指定方式相关的唯一修订对(在这种情况下,更改方法和其中一个方法调用另一个)。它运行良好,只是非常慢(运行时间约 10 分钟)。我基本上是在寻找任何建议或帮助,以在不牺牲准确性的情况下更好地完成这项工作。

【问题讨论】:

  • 你有所有主键/外键的索引吗?每个表有多少行?
  • methodinvocation 中有多少条记录?
  • 所有键都被索引。方法调用表中有大约 110K 条目。 deltamethoddeclaration 有 120K 条目,revision 有 14K
  • @Kyle P - 您有什么理由使用 Case 表达式而不是 aw.same = 1 And r1.author = r2.author
  • 因为我也想要 aw.same 不是 1 的结果,而且这些结果不应该问作者是否相同。我一定是误解了你的问题。

标签: mysql sql query-optimization


【解决方案1】:
  • 哪里(dmd1.FKrevID - dmd2.FKrevID)

    这个语句最具破坏性的是小于运算符&lt;,而不是算术。 B-trees 不能使用它,并且每次都强制进行全表扫描。血淋淋地详细说明了为什么这是真的:http://explainextended.com/2010/05/19/things-sql-needs-determining-range-cardinality/

  • 我怀疑您的CASE 语句可以由后端优化,&lt;&gt; 运算符遇到与上述相同的问题。我会考虑加入 = 运算符的方法,可能会分解查询并使用 UNION 语句,以便您始终可以使用索引。

  • 你没有使用EXPLAIN。您需要开始使用它来优化查询。您不知道正在使用哪些索引,哪些没有,或者您的条件是否有足够的选择性,它们甚至会有所帮助(如果不是很有选择性,请参阅最后一点)http://dev.mysql.com/doc/refman/5.0/en/explain.html

  • 由于这是一个数据挖掘应用程序,您有很好的机会使用中间值的临时表。由于数据可能会定期转储(或者甚至可能只转储一次!),因此很容易每隔一段时间重建长时间运行的临时表,而不会冒数据损坏的风险(或者它可能无关紧要,因为您正在寻找聚合模式.)

    我已经通过构建缓存硬数据的临时表将运行时间超过 60 分钟的查询缩短到不到 100 毫秒(即时)。如果您无法使用上述任何想法,这可能是最低的水果。把所有“困难的东西”——案例连接和不等式连接放在一个地方。然后为您的临时表添加一个索引 :-) 诀窍是使其足够通用,以便您可以查询临时表,以便您仍然可以灵活地提出不同的问题。

【讨论】:

  • nate,感谢 cmets。很明显我以前没有做过这样的事情,所以你能给我一个关于如何解决这个问题的更具体的指示吗?由于它减去两个键,我仍然可以使用您第一个链接中讨论的范围技巧吗?或者你能给我一个例子,说明在临时表中放入什么以及如何使用它?
  • @Kyle:假设您可以使用预先计算的索引,但在这种情况下它不起作用,因为它们只能使用一个表中的常量和值(而不是自连接)。为它创建一个临时表然后添加索引可能是一个不错的选择。
  • @Kyle:不确定索引是否是您的主要问题,因为看起来您正在扫描大多数行而没有太多选择性。无论您做什么,都可能会导致全表扫描。我会认真考虑使用 id 的差异和相同的作者案例表达式预先计算一个临时表。如果您可以预先聚合数据,您可能会使查询工作得非常快。然后用修订和作者窗口的东西查询临时表。
【解决方案2】:

我怀疑没有 ON 条件但使用 WHERE 的两个连接 (join revisionwindow rw) 和 (join authorwindow aw) 导致了这种情况。

这两个表有多少条记录? MySQL 可能首先对这些进行 CROSS JOIN,然后才检查复杂的 (WHERE) 条件。

但是请把EXPLAIN的结果贴出来。

--编辑--
糟糕,我错过了你的最后一段,它解释了这两个表有 4 行和 3 行。

你可以试试这个: (其中 concat 已被替换 并且 where 子句已被移动为 JOIN ON ...)

select aw.same
     , rw.k
     , count(distint r1.id, r2.id) as num  
  from deltamethoddeclaration dmd1
    join revision r1
      on r1.id = dmd1.FKrevID 
    join methodinvocation mi
      on mi.FKcallerID = dmd1.FKMDID 
    join deltamethoddeclaration dmd2 
      on mi.FKcalleeID = dmd2.FKMDID
    join revision r2 
      on r2.id = dmd2.FKrevID
    join revisionwindow rw
      on (dmd1.FKrevID - dmd2.FKrevID) < rw.k
         and (dmd2.FKrevID - dmd1.FKrevID) < rw.k
    join authorwindow aw
      on case aw.same
           when 1 then
             r1.author = r2.author
           when 0 then
             r1.author <> r2.author
           else
             1=1
          end
  group by aw.same
         , rw.k
;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-16
    • 2014-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-18
    相关资源
    最近更新 更多