代码之家  ›  专栏  ›  技术社区  ›  Garrett

Sybase-除非字符串硬编码,否则无法使用索引

  •  2
  • Garrett  · 技术社区  · 14 年前

    我在用 Sybase 12.5.3(ASE) ;我刚接触Sybase,不过我与MSSQL的合作非常广泛。我遇到了一个存储过程非常慢的场景。对于一个相对较大的表,我跟踪到了一个select stmt问题。修改该语句会极大地提高过程的性能(恢复该语句会极大地降低速度;也就是说,select stmt肯定是罪魁祸首)。

    -- Sybase optimizes and uses multi-column index... fast!<br>
    SELECT ID,status,dateTime
    FROM myTable
    WHERE status in ('NEW','SENT')
    ORDER BY ID
    
    -- Sybase does not use index and does very slow table scan<br>
    SELECT ID,status,dateTime
    FROM myTable
    WHERE status in (select status from allowableStatusValues)
    ORDER BY ID
    

    上面的代码是实际代码的改编/简化版本。请注意,我已经尝试重新编译过程、更新统计信息等。

    我不知道为什么SybaseASE只在字符串硬编码时选择一个索引,在从另一个表中选择时选择一个表扫描。有人请给我一个线索,并提前感谢你。

    6 回复  |  直到 11 年前
        1
  •  2
  •   PerformanceDBA    14 年前

    1.这里的问题是编码不良。在您发布的版本中,糟糕的代码和糟糕的表设计是优化者做出错误决策的主要原因(98%)(这两个问题是同时发生的,我没有计算出每个问题的比例)。两者:

        WHERE status IN ('NEW','SENT')
    

        WHERE status IN (SELECT status FROM allowableStatusValues)
    

    不符合标准,因为在这两种情况下,它们都会导致ASE为括号之间的内容创建一个工作表,这很容易避免(并且避免了所有后续问题)。工作台上不可能有统计数据,因为T.status或S.status的统计数据丢失(Adamh在该点上是正确的),所以它正确地选择了表扫描。

    子查询有它们的位置,但不能代替纯(表 相关)加入。更正如下:

        WHERE status = "NEW" OR status = "SENT"
    

        FROM  myTable t,
              allowableStatusValues s
        WHERE t.status = s.status
    

    2、声明

    γ 现在您不必添加索引来获取列的统计信息,但是 这可能是最好的方法。

    是不正确的。不要创建不使用的索引。如果要更新列的统计信息,只需

        UPDATE STATISTICS myTable (status)
    

    3.重要的是确保您拥有(a)所有索引列和(b)所有联接列的当前统计信息。

    4.是的,在每个要发布的代码段上都没有ShowPlan的替代品,对于任何性能有问题的代码都是如此。你也可以 SET NOEXEC ON 以避免执行,例如对于大型结果集。

        2
  •  2
  •   AdamH    14 年前

    索引提示可以解决这个问题,但可能不是解决方案。

    首先,我想知道allowablestatusvalues.status上是否有索引,如果有,那么Sybase将对其进行统计,并对其中的值的数量有一个好主意。 如果不是这样,乐观主义者可能不会很好地知道状态需要多少不同的值。然后必须假设您将要从MyTable中提取几乎所有的行,最好的方法是表扫描(如果没有覆盖索引的话)。

    现在您不必添加索引来获取列的统计信息,但这可能是最好的方法。

    如果您对allowableStatusValues.status有一个索引,那么我想知道您的状态有多好。给自己一份 sp__optdiag . 您可能还需要调整“柱状图调整因子”和“柱状图步数”的值,从默认值中稍微增加这些值将提供更详细的统计信息,这始终有助于优化。

        3
  •  1
  •   HLGEM    14 年前

    如果用join替换子查询,它是否仍然进行表扫描:

    SELECT m.ID,m.status,m.dateTime 
    FROM myTable m
    JOIN allowableStatusValues a on m.status = a.status
    ORDER BY ID 
    
        4
  •  0
  •   ninesided    14 年前

    我强烈建议让Sybase向您展示每个查询的执行计划,而不是依赖于运行查询所需时间的实验观察,例如:

    SET showplan ON
    GO
    
    -- query/procedure call goes here
    SELECT id, status, datetime
    FROM myTable
    WHERE status IN('NEW','SENT')
    ORDER BY id
    GO
    
    SET showplan OFF
    GO
    

    SET showplan ON ,Sybase为它执行的每个语句生成执行计划。这些在帮助确定查询没有使用适当索引的位置上是非常有价值的。对于Sybase中的存储过程,整个过程的执行计划是在编译后第一次执行存储过程时生成的。

    如果您发布每个查询的计划,我们可能会更清楚地了解这个问题。

        5
  •  0
  •   Garrett    14 年前

    令人惊讶的是,使用 索引提示 解决问题(请参阅 (索引myindexname) 下一行-重新编写/简化以下代码:

    -- using INDEX HINT
    SELECT ID,status,dateTime 
    FROM myTable (index myIndexName)
    WHERE status in (select status from allowableStatusValues) 
    ORDER BY ID 
    

    奇怪的是我 使用这种技术来避免扫描表格,但现在可以了。

        6
  •  0
  •   Phlamingo    14 年前

    加勒特,通过只显示简化的代码,您可能已经完全删除了可以说明问题根源的信息。

    我的第一个猜测是allowableStatusValues.status和myTable.status之间的类型不匹配。然而,这并不是唯一的可能性。如所述,完整的查询计划(使用showplan和fmtonly标志)以及实际的表定义和存储过程源更可能生成有用的答案。