【问题标题】:How does SQL server work out the estimated number of rows?SQL Server 如何计算出估计的行数?
【发布时间】:2009-09-25 11:13:38
【问题描述】:

我正在尝试调试一个相当复杂的存储过程,它连接了许多表 (10-11)。我看到对于树的一部分,估计的行数与实际的行数有很大的不同——在最坏的情况下,SQL 服务器估计将返回 1 行,而实际上返回了 55,000 行!

我正在尝试找出原因 - 我的所有统计信息都是最新的,并且我已经使用 FULLSCAN 更新了几个表格上的统计信息。我没有使用任何用户定义的函数或表变量。据我所见,SQL Server 应该能够准确估计将要返回多少行,但它会继续选择一个计划来执行数万次 RDI 查找(当它预计只执行 1或 2)。

我可以做些什么来尝试理解为什么估计的行数超出了这么多?

更新:因此,查看计划时,我发现了一个特别可疑的节点 - 它使用以下 predecate 对表进行表扫描:

status <> 5
AND [type] = 1
OR [type] = 2

这个谓词返回整个表(630 行 - 表扫描本身不是性能不佳的根源)但是 SQL 服务器的估计行数只有 37。然后 SQL 服务器继续执行几个嵌套循环这将用于 RDI 查找、索引扫描和索引搜索。这会是我严重误判的根源吗?如何让它估计更合理的行数?

【问题讨论】:

  • 能否请您发布您的表定义和完整的查询?
  • 对不起,但不是真的 - 它太大了(250 行 sp + 10 个表)。
  • 如果你的谓词完全一样(没有括号),那么你可能有逻辑问题。 AND 优先于 OR。应该是 [status] 5 AND (type = 1 OR type = 2)

标签: sql-server sql-server-2005 sql-execution-plan


【解决方案1】:

SQL Server 将每个索引拆分为最多 200 的范围,其中包含以下数据(来自 here):

  • RANGE_HI_KEY

    显示直方图步骤上边界的键值。

  • RANGE_ROWS

    指定范围内的行数(它们小于此RANGE_HI_KEY,但大于先前较小的RANGE_HI_KEY)。

  • EQ_ROWS

    指定有多少行完全等于RANGE_HI_KEY

  • AVG_RANGE_ROWS

    范围内每个不同值的平均行数。

  • DISTINCT_RANGE_ROWS

    指定此范围内有多少不同的键值(不包括RANGE_HI_KEYRANGE_HI_KEY 本身之前的前一个键);

通常,大多数填充值进入RANGE_HI_KEY

但是,它们可以进入范围,这可能导致分布偏斜。

想象一下这些数据(以及其他数据):

键值行数

1          1
2          1
3          10000
4          1

SQL Server 通常构建两个范围:134 到下一个填充值,这会产生这些统计信息:

RANGE_HI_KEY  RANGE_ROWS  EQ_ROWS  AVG_RANGE_ROWS  DISTINCT_RANGE_ROWS
3             2           10000    1               2

,表示搜索2时,只有1行,最好使用索引访问。

但如果3 进入范围内,则统计数据如下:

RANGE_HI_KEY  RANGE_ROWS  EQ_ROWS  AVG_RANGE_ROWS  DISTINCT_RANGE_ROWS
4             10002       1        3334            3

优化器认为键 23334 行,索引访问成本太高。

【讨论】:

  • 即使使用全扫描更新统计信息也无法解决这个问题,如何解决?
  • @Maysam:您可以将CREATE STATISTICS 用于您经常使用的谓词。
【解决方案2】:

它使用为每个索引保留的统计信息。

(您也可以在非索引列上创建统计信息)

要更新数据库中每个表的所有统计信息(警告:在非常大的数据库上需要一些时间。如果没有与 DBA 核实,请勿在生产服务器上执行此操作...) :

exec sp_msforeachtable 'UPDATE STATISTICS ?'

如果您没有定期计划的作业来重建最活跃的索引(即大量的 INSERTS 或 DELETES),您应该考虑重建索引(与上述相同的警告适用):

exec sp_msforeachtable "DBCC DBREINDEX('?')"

【讨论】:

    【解决方案3】:

    由于您已经更新了统计信息,我会尝试消除任何参数嗅探:

    CREATE PROCEDURE xyz
    (
        @param1 int
        ,@param2 varchar(10)
    
    )AS
    
    DECLARE @param_1 int
           ,@param_2 varchar(10)
    
    SELECT @param_1=@param1
          ,@param_2=@param2
    
    ...complex query here....
    ...WHERE column1=@param_1 AND column2=@param_2....
    
    go
    

    【讨论】:

      【解决方案4】:

      重建索引可能会解决不正确的估计行值问题

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-19
        • 2011-04-06
        • 2023-04-08
        • 2016-05-05
        • 1970-01-01
        • 2023-03-31
        相关资源
        最近更新 更多