为什么这是Sproc扫描表

时间:2013-08-22 21:03:06

标签: sql sql-server tsql

这个系统是几年前在SQL7中构建的,目前在SQL2k5中运行

我们有一个表格产品,其中包含一个骄傲的Guid作为PK / Clustered Index。我意识到为了获得最佳性能,我们应该修改表格,但此时不可能。我将它加入到tProductSpecial表中,该表还有一个PK / Clustered Index of productid,这也是这种关系中的FK。我们在tProducts表中有大约50k记录,在tProductSpecial表中有大约35k记录(一些产品有特殊信息,有些没有)。还有一件。我在sproc中使用临时表来获取登录用户安全角色并加载它们,这也加入了tProducts表,roleid是tProducts中的非聚集索引。我已经包含了一些访问这些表的WHERE条件。

SELECT *
FROM tProducts
  JOIN tProductSpecial ON tProducts.productid=tProductSpecial.productid
  JOIN #tRoles ON tProducts.roleid=#tRoles.roleid
WHERE
  (tProducts.productSKU = @sku AND tProducts.productStatus=1)  --DIRECT MATCH
  OR
  (  -- KEYWORD SEARCH
    CONTAINS(tProducts.*,'FORMSOF(INFLECTIONAL,''' + @lookuptext + ''')') 
    AND
    (
      @productStatus IS NULL
      OR
      (
        @productStatus IS NOT NULL
        AND
        tProducts.productStatus = @productStatus
      )          
    )
    AND 
    (  --- item on sale
      @bOnSale IS NULL
      OR @bOnSale=0
      OR
      (
        @bOnSale = 1
        AND tProducts.productOnSale=1
      )
    )
    AND
    (  -- from price
      @from=0 
      OR @from IS NULL
      OR 
      (
        @from<>0
        AND             
        tProducts.customerCost>=@from
      )         
    )
    AND
    (  --to price
      @to=0 
      OR @to IS NULL
      OR 
      (
        @to<>0
        AND             
        tProducts.customerCost<=@from
      )         
    )
    AND
    (  --how old is product
      @age IS NULL 
      OR @age = 0       
      OR
      (
        @age IS NOT NULL
        AND @age > 0
        AND DATEDIFF(day,tProducts.productCreated,GETDATE()) 
          <=CONVERT(varchar(10),@age)       
       )
     )

  ORDER BY tProducts.productSKU

2 个答案:

答案 0 :(得分:2)

问题可能是由于参数嗅探 - 即使参数不同,你也会一遍又一遍地使用相同的计划,有时只扫描是最好的方法。在SQL Server 2008中,您只需添加OPTION (RECOMPILE)OPTIMIZE FOR UNKNOWN,但在SQL Server 2005中,请尝试更改存储过程并添加WITH RECOMPILE选项。这将迫使SQL Server每次都根据传入的参数考虑新的计划。

另一种选择是根据是否填充@bOnSale@from等动态建立查询。在2005年,这将导致计划缓存膨胀,但总体而言你可能会更好。这可以完全避免全文访问,例如,填充@sku时。同样,在SQL Server 2008中,这样做会更好,因为您可以使用Optimize for ad hoc workloads来避免某些计划缓存膨胀。

答案 1 :(得分:0)

长期存储的程序可能会因各种原因而突然发展出一个糟糕的计划案例。在我的头顶,有一些突出:

索引更改

删除/修改表上的索引可能会导致具有完美罚款执行计划多年的查询向南移动。解决方案:不要这样做。如果您要这样做,请检查该表的依赖关系,并确保所有内容仍然是一个良好的执行计划。

表统计

由于多种原因,索引统计数据可能会被污染。例如,存储过程引用的表中的大量数据加载可以对该表的索引统计信息进行大量提升,从而误导查询优化器构建不良的执行计划。解决方案包括以下一项或多项:

  • 更新所涉及的表格的统计数据
  • 运行DBCC FREEPROCCACHE以刷新存储过程缓存。
  • 对违规存储过程执行sp_recompile

直接引用存储过程参数

在创建和缓存计划时,其查询直接使用传递到存储过程的参数的存储过程将根据存储过程首次执行时参数的值获取执行计划。如果这些参数值有点outré,则缓存的执行计划可能对奇怪的原因表现良好,但对于一般情况则非常差。短期解决方案是torun sp_recompile或修改存储过程以便具有with recompile选项(注意,这会对性能产生重大影响,因为存储过程会在每次执行时重新编译。这意味着在编译过程中会进行编译锁定,这会导致阻塞。

此问题的正确解决方法是在存储过程中声明局部变量并将其设置为参数的值。这会将参数值转换为表达式,并破坏计划对参数值的依赖性。我第一次遇到这个问题时就把我弄坏了。

祝你好运!