【问题标题】:This transitive_closure query takes 20 seconds to complete! Why?这个transitive_closure 查询需要20 秒才能完成!为什么?
【发布时间】:2011-06-28 15:32:42
【问题描述】:

当我在 Member_Contact_Edges 表中几乎有 500 行时,此查询工作得非常好并且快速完成。但是现在,我在这个表中有近 1.000 行,这个查询需要 20-30 秒才能完成。我无法弄清楚问题出在哪里。我尝试了聚集和非聚集索引。我尝试了每种索引组合,但都没有成功。

 ;WITH transitive_closure(member_a, member_b, distance, path_string) AS

  (SELECT member_a, member_b, 1 AS distance, CAST(member_a as varchar(MAX)) + '.' + CAST(member_b as varchar(MAX)) + '.' AS path_string
          FROM Member_Contact_Edges
          WHERE member_a = @source AND contact_durum=1 -- source

   UNION ALL

   SELECT tc.member_a, e.member_b, tc.distance + 1, CAST(tc.path_string as varchar(MAX)) + CAST(e.member_b as varchar(MAX)) + '.' AS path_string
          FROM Member_Contact_Edges AS e
          JOIN transitive_closure AS tc ON e.member_a = tc.member_b
          WHERE tc.path_string NOT LIKE '%' + CAST(e.member_b as varchar(MAX)) + '.%' AND e.contact_durum=1
   )

   SELECT distance, path_string FROM transitive_closure
          WHERE member_b=@target AND distance <= 3 -- destination
          ORDER BY member_a, member_b, distance;

这就是我所说的存储过程:

   Exec Contacts_KacinciDerece @source = 30284, @target=24688

输出:(这是我所期望的,这个查询创建了这个)

谢谢。

【问题讨论】:

  • 在递归 CTE(这就是您在此处所拥有的)中,将源表中的记录数加倍可能会导致处理时间呈指数增长,因为它是针对自身进行评估的。将行数加倍意味着它正在比较 3 倍的行数(500*500 与 1000*1000)
  • 更正 - 行数的 4 倍,250k vs 1m

标签: sql-server optimization transactions performance


【解决方案1】:

你有一个path_string NOT LIKE '%' + CAST(e.member_b as varchar(MAX)) + '.%'

  • 没有办法优化 NOT LIKE 或前导通配符 LIKE,更不用说两者了
  • path_string 无论如何都是计算字段

Member_Contact_Edges 中的每一行额外增加了必须扫描的行数(平方),而没有任何索引的好处。

这个O(n^2)至少:我怀疑更高...

【讨论】:

  • +1 - 很少有东西能像递归 CTE 一样快速显示优化问题 - 当你的坏代码是由其他坏代码构建时,它会很快变慢。
  • 没错,有什么建议可以得到这样的输出吗?我为此问题打开了一个线程,但除了递归 CTE Previous post - Source 之外,我找不到任何其他解决方案
  • 比较 e.member_b 和 tc.member_b 而不是 tc.path_string。
  • @Phil Helmer 所以你认为这里的主要性能问题是 NOT LIKE 声明?我对吗?让我试试看。
  • @Phil Helmer 我无法运行已编辑的查询。 'JOIN transitive_closure AS tc ON e.member_a = tc.member_b AND e.member_btc.member_b WHERE e.contact_durum=1' 错误描述:语句终止。在语句完成之前,最大递归 100 已用完。
【解决方案2】:

看起来 NOT LIKE 语句是性能的关键。通过限制距离,我设法在 160 毫秒内运行查询。

但是这里出现了问题:

如果我不使用 NOT LIKE 语句,由于递归选择,它会选择同一个人两次或更多次。

例如;

;WITH transitive_closure(member_a, member_b, distance, path_string) AS

(SELECT member_a, member_b, 1 AS distance, CAST(member_a as varchar(MAX)) + '.' + CAST(member_b as varchar(MAX)) + '.' AS path_string
      FROM Member_Contact_Edges
      WHERE member_a = @source AND contact_durum=1 -- source

UNION ALL

SELECT tc.member_a, e.member_b, tc.distance + 1, CAST(tc.path_string as varchar(MAX)) + CAST(e.member_b as varchar(MAX)) + '.' AS path_string
      FROM Member_Contact_Edges AS e
      JOIN transitive_closure AS tc ON e.member_a = tc.member_b
      WHERE tc.member_b <> e.member_b AND tc.distance<4 AND e.contact_durum=1
)

SELECT distance, path_string FROM transitive_closure
      WHERE member_b=@target AND distance < 4 -- destination
      ORDER BY member_a, member_b, distance;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-24
    • 2011-03-25
    • 2016-12-11
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多