简单查询中的SQL CASE语句导致极端缓慢

时间:2015-01-25 17:55:45

标签: sql asp.net .net sql-server performance

我构建了一个非常简单的查询,在使用CASE语句时使用时非常长。你能提出任何见解吗?

示例1:没有CASE语句。运行< 1秒

select * from
(
    select  
        (row_number() over (order by b.BookName)) as RowNumber,
        b.*     
    from
        Books b (nolock) 
        inner join BookPublishRegions p (nolock)
          on b.BookKey = bp.BookKey
    where       
        contains(p.PublishRegionName, 'France')
) as t1
where t1.RowNumber between 100 and 110

示例2:使用CASE语句需要30秒以上

select * from
(
    select  
        case @SortBy    
            when 'Price' then
                (row_number() over (order by b.Price))
            else
                (row_number() over (order by b.BookName))           
        end as RowNumber,
        b.*     
    from
        Books b (nolock) 
        inner join BookPublishRegions p (nolock)
          on b.BookKey = bp.BookKey
    where       
        contains(p.PublishRegionName, 'France')
) as t1
where t1.RowNumber between 100 and 110

我实际上使用上述硬编码值按原样运行这些查询。硬编码值的参数替换没有任何好处。

编辑:明确选择列而不是b。*也没有任何好处。

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

你能试试这个版本吗?

select * from
(
    select row_number() over (order by b.Price) as rn_price,
           row_number() over (order by b.BookName) as rn_bookname,    
           b.*     
    from
        Books b (nolock) 
        inner join BookPublishRegions p (nolock)
          on b.BookKey = bp.BookKey
    where       
        contains(p.PublishRegionName, 'France')
) as t1
where (t1.rn_price between 100 and 110 and @SortBy = 'Price') or
      (t1.rn_name between 100 and 110 and @SortBy <> 'Price')

我怀疑这也会很慢。实际上,您可能遇到无法使用排序选项加速此查询的情况。我想你有Books(BookName)Books(Price)的索引。这些索引用于join,因为SQL Server足够聪明,可以识别索引对row_number()有用,因此按顺序返回行是一个很好的优化。那么问题是只能使用其中一个索引,因此case逻辑(或多个行号)会阻止这种优化。

这只是一个猜测,但它可能会使您的查询更加困难。

如果这是问题,那么您可以使用动态SQL解决它。这将为特定选项创建SQL,优化器将做正确的事情。

答案 1 :(得分:0)

如果您使用的是Sql Server 2012+,请使用OFFSET-FETCH方法代替Row_number。试试这个。

SELECT b.*
FROM   Books b (nolock)
       INNER JOIN BookPublishRegions p (nolock)
               ON b.BookKey = bp.BookKey
WHERE  CONTAINS(p.PublishRegionName, 'France')
ORDER  BY ( CASE
              WHEN @SortBy = 'Price' THEN Price
            END ),
          ( CASE
              WHEN @SortBy = 'Price' THEN NULL
              ELSE b.BookName
            END ) 
            OFFSET 99 ROWSFETCH NEXT 11 ROWS ONLY;

答案 2 :(得分:0)

我无法测试此查询,但我认为将案例INSIDE放在row_number函数中可以解决您的问题。

select * from
(
    select  
        (row_number() over (case @SortBy when 'Price' then b.Price else b.BookName end))           
        as RowNumber,
        b.*     
    from
        Books b (nolock) 
        inner join BookPublishRegions p (nolock)
          on b.BookKey = bp.BookKey
    where       
        contains(p.PublishRegionName, 'France')
) as t1
where t1.RowNumber between 100 and 110