【问题标题】:What can I do to speed this slow query up?我能做些什么来加快这个慢查询?
【发布时间】:2013-04-19 17:32:02
【问题描述】:

我们有一个庞大的多表 Sybase 查询,我们称之为 get_safari_exploration_data 查询,它获取与探险者进行 safari 以及他们遇到的所有动物相关的各种信息。

这个查询很慢,我被要求加快速度。我首先想到的是,在外部 FROM 子句中似乎并不迫切需要嵌套的 SELECT 语句。在嵌套的SELECT 中,似乎还有几个不需要的字段(vegetablebroomhildadevoured 等)。我也对连接的使用持怀疑态度(“*=”而不是“INNER JOIN...ON”)。

SELECT
    dog_id,
    cat_metadata,
    rhino_id,
    buffalo_id,
    animal_metadata,
    has_4_Legs,
    is_mammal,
    is_carnivore,
    num_teeth,
    does_hibernate,
    last_spotted_by,
    last_spotted_date,
    purchased_by,
    purchased_date,
    allegator_id,
    cow_id,
    cow_name,
    cow_alias,
    can_be_ridden
FROM
(
    SELECT
        mp.dog_id as dog_id,
        ts.cat_metadata + '-yoyo' as cat_metadata,
        mp.rhino_id as rhino_id,
        mp.buffalo_id as buffalo_id,
        mp.animal_metadata as animal_metadata,
        isnull(mp.has_4_Legs, 0) as has_4_Legs,
        isnull(mp.is_mammal, 0) as is_mammal,
        isnull(mp.is_carnivore, 0) as is_carnivore,
        isnull(mp.num_teeth, 0) as num_teeth,
        isnull(mp.does_hibernate, 0) as does_hibernate,
        jungle_info.explorer as last_spotted_by,
        exploring_journal.spotted_datetime as last_spotted_date,
        jungle_info.explorer as purchased_by,
        early_exploreration_journal.spotted_datetime as purchased_date,
        alleg_id as allegator_id,
        ho.cow_id,
        ho.cow_name,
        ho.cow_alias,
        isnull(mp.is_ridable,0) as can_be_ridden,
        ts.cat_metadata as broomhilda,
        ts.squirrel as vegetable,
        convert (varchar(15), mp.rhino_id) as tms_id,
        0 as devoured
    FROM
        mammal_pickles mp,
        very_tricky_animals vt,
        possibly_venomous pv,
        possibly_carniv_and_tall pct,
        tall_and_skinny ts,
        tall_and_skinny_type ptt,
        exploration_history last_exploration_history,
        master_exploration_journal exploring_journal,
        adventurer jungle_info,
        exploration_history first_exploration_history,
        master_exploration_journal early_exploreration_journal,
        adventurer jungle_info,
        hunting_orders ho
    WHERE
        mp.exploring_strategy_id = 47
        and mp.cow_id = ho.cow_id
        and ho.cow_id IN (20, 30, 50)
        and mp.rhino_id = vt.rhino_id
        and vt.version_id = pv.version_id
        and pv.possibly_carniv_and_tall_id = pct.possibly_carniv_and_tall_id
        and vt.tall_and_skinny_id = ts.tall_and_skinny_id
        and ts.tall_and_skinny_type_id = ptt.tall_and_skinny_type_id
        and mp.alleg_id *= last_exploration_history.exploration_history_id
        and last_exploration_history.master_exploration_journal_id *= exploring_journal.master_exploration_journal_id
        and exploring_journal.person_id *= jungle_info.person_id
        and mp.first_exploration_history_id *= first_exploration_history.exploration_history_id
        and first_exploration_history.master_exploration_journal_id *= early_exploreration_journal.master_exploration_journal_id
        and early_exploreration_journal.person_id *= jungle_info.person_id
) TEMP_TBL

所以我问:

  • 我对嵌套的SELECT 是否正确?
  • 我是否纠正了嵌套 SELECT 中不必要的字段?
  • 我对连接的结构/语法/用法是否正确?
  • 关于这个查询的结构/性质,还有什么其他让您觉得效率低下/速度慢的地方吗?

不幸的是,除非有无可辩驳的事实证明,从长远来看,将这个大型查询分解为更小的查询是有益的,否则管理层根本不会批准将其重构为多个更小的查询,因为这将花费大量时间是时候重构和测试了。在此先感谢您提供的任何帮助/见解!

【问题讨论】:

  • 嵌套的 SELECT 可能会也可能不会提供性能影响。这取决于执行计划(我不能告诉你,因为要建模的表很多)。如果它使用的是临时表,那么一定要删除嵌套的 SELECT(您可以使用 EXPLAIN 计划找到)。否则,额外的字段不会伤害任何东西,除非它们需要不必要的连接。同样,这取决于执行计划是否使用临时表(如果子查询没有首先复制到临时表,优化器将从查询中删除不必要的表)。
  • 感谢 @jtv4k (+1) - 澄清一下,您是说 if 执行计划(我可以通过 EXPLAIN 看到)显示 Sybase 使用TEMP_TBL,那我肯定要去掉嵌套的SELECT?如果这就是你的意思,你能解释为什么嵌套的SELECT 和关联的TEMP_TBL 对执行计划来说是“坏”(慢)吗?
  • 啊,我开始“明白”了。如果我理解正确:如果子查询被复制到临时表,那么优化器将不会删除不必要的连接;否则不必要的连接将作为执行计划的一部分保留,它们会对性能产生不利影响? 这是一个公平的评估吗?再次感谢!
  • 这是个玩笑吗?如果是的话,那就太搞笑了!
  • 这有“管理”?!?

标签: sql performance optimization sybase


【解决方案1】:
  • 我对嵌套 SELECT 的看法是否正确?

在某些情况下你可能会这样做,但一个称职的计划者会在这里折叠并忽略它。

  • 我是否纠正了嵌套 SELECT 中不必要的字段?

是的,特别是考虑到其中一些根本没有出现在最终的字段列表中。

  • 我对连接的结构/语法/用法是否正确?

据我所知,*==* 只是左右连接的语法糖,但我这样说可能是错误的。如果不是,那么它们只是强制连接发生的方式,但它们可能是您的查询工作所必需的。

  • 关于这个查询的结构/性质,还有什么其他让您觉得效率低下/速度慢的地方吗?

是的。

首先,您有一些不需要的计算,例如convert (varchar(15), mp.rhino_id) as tms_id。也可能有一两个连接,但我承认我还没有查看查询的血腥细节。

接下来,您可能会遇到数据库设计本身的问题,例如一个 cow_id 字段。 (严重吗?:-))

最后,为了避免进行大量的连接,有时需要说明执行多个查询而不是单个查询。

例如,在博客中,获取前 10 个帖子通常是个好主意,然后使用单独的查询来获取它们的标签(其中 id 在(id1、id2 等)中)。在您的情况下,选择性部分似乎就在这里:

    mp.exploring_strategy_id = 47
    and mp.cow_id = ho.cow_id
    and ho.cow_id IN (20, 30, 50)

因此,也许可以在一个查询中隔离该部分,然后使用生成的 ID 构建一个 in () 子句,并在一个或多个单独的查询中获取装饰性的点点滴滴。

哦,正如 Gordon 所指出的,还要检查您的索引。但是,请注意,如果不将查询拆分为更易于管理的部分,索引可能最终没什么用处。

【讨论】:

    【解决方案2】:

    我会建议以下方法。

    首先,使用带有on 子句的ANSI 标准连接重写查询。这将使条件和过滤更容易理解。此外,这是“安全的”——您应该得到与当前版本完全相同的结果。小心,因为*= 是外连接,所以不是所有的都是内连接。

    我怀疑这一步会提高性能。

    然后,检查每个引用表并确保连接键在引用表中具有索引。如果缺少密钥,请添加它们。

    然后,检查左外连接是否是必要的。左外部联接的表上有过滤器。 . .这些过滤器将外连接转换为内连接。可能不会影响性能,但你永远不知道。

    然后,考虑索引用于过滤的字段(在where 子句中)。

    并且,学习如何使用解释功能。任何嵌套循环连接(没有索引)都可能是性能问题的罪魁祸首。

    至于嵌套选择,我认为 Sybase 足够聪明,可以“做正确的事”。即使它写出并重新读取结果集,与正确连接相比,这可能对查询产生边际影响。

    如果这是你真正的数据结构,顺便说一下,这听起来像是一个非常有趣的领域。我很少在表格中看到名为allegator_id 的字段。

    【讨论】:

    • 这些都是很好的建议,但你不是治标不治本吗?假设这是一个真实的问题,那么应该首先解决数据库设计方面的一些大问题。
    【解决方案3】:

    我会回答你的一些问题。

    您认为嵌套 SELECT 中的字段(vegetable、broomhilda、devoured)可能会导致性能问题。不必要。嵌套 SELECT 中两个未使用的字段(vegetable、broomhilda)来自 ts 表,但正在使用的 cat_metadata 字段也来自 ts 表。因此,除非 cat_metadata 被 ts 表上使用的索引覆盖,否则不会对性能产生任何影响。因为,要提取 cat_metadata 字段,无论如何都需要从表中获取数据页。其他两个字段的提取将占用很少的 CPU,仅此而已。所以不用担心。 “吞噬”场也是一个常数。这也不会影响性能。

    Dennis 指出了转换函数 convert(varchar(15), mp.rhino_id) 的用法。我不同意这会影响性能,因为它只会消耗 CPU。

    最后我想说,尝试使用设置的表数为 13,因为那里有 13 个表。 Sybase 一次使用四个表进行优化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-12
      • 1970-01-01
      • 2013-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-30
      相关资源
      最近更新 更多