SQL-为什么在ORDERBY子句中添加CASE会显著降低性能?我怎样才能避免这种情况?

SQL-为什么在ORDERBY子句中添加CASE会显著降低性能?我怎样才能避免这种情况?,sql,sql-server,Sql,Sql Server,我希望改进我们的一个存储过程的性能,但遇到了一些我很难找到相关信息的问题。我绝不是DBA,所以我对SQL的知识并不渊博 下面是问题的简化版本: 如果我使用以下查询- SELECT * FROM Product ORDER BY Name OFFSET 100 ROWS FETCH NEXT 28 ROWS ONLY 我在20毫秒左右得到结果 但是,如果我应用条件排序- DECLARE @so int = 1 SELECT * FROM Product ORDER BY CASE WHEN

我希望改进我们的一个存储过程的性能,但遇到了一些我很难找到相关信息的问题。我绝不是DBA,所以我对SQL的知识并不渊博

下面是问题的简化版本:

如果我使用以下查询-

SELECT * FROM Product
ORDER BY Name
OFFSET 100 ROWS
FETCH NEXT 28 ROWS ONLY
我在20毫秒左右得到结果

但是,如果我应用条件排序-

DECLARE @so int = 1

SELECT * FROM Product
ORDER BY 
CASE WHEN @so = 1 THEN Name END,
CASE WHEN @so = 2 THEN Name END DESC
OFFSET 100 ROWS
FETCH NEXT 28 ROWS ONLY
在我看来,总体要求是一样的,但结果需要600毫秒,30倍的时间

执行计划完全不同,但作为一个新手,我不知道如何使第二个案例的执行路径和第一个案例一致

这是可能的,还是我应该考虑为order by CASE创建单独的过程,并将选择顺序逻辑转移到代码中


注意。这是使用MS SQL Server的原因是SQL Server无法再使用索引。一种解决方案是动态SQL。另一个是简单的
,如果

IF (@so = 1)
BEGIN
    SELECT p.*
    FROM Product p
    ORDER BY Name
    OFFSET 100 ROWS
    FETCH NEXT 28 ROWS ONLY
END;
ELSE
BEGIN
    SELECT p.*
    FROM Product p
    ORDER BY Name DESC
    OFFSET 100 ROWS
    FETCH NEXT 28 ROWS ONLY;
END:

原因是SQL Server无法再使用索引。一种解决方案是动态SQL。另一个是简单的
,如果

IF (@so = 1)
BEGIN
    SELECT p.*
    FROM Product p
    ORDER BY Name
    OFFSET 100 ROWS
    FETCH NEXT 28 ROWS ONLY
END;
ELSE
BEGIN
    SELECT p.*
    FROM Product p
    ORDER BY Name DESC
    OFFSET 100 ROWS
    FETCH NEXT 28 ROWS ONLY;
END:

Gordon Linoff说得对,这会阻止索引被使用,但要在此基础上进行扩展:

当SQL Server准备执行查询时,它会生成一个执行计划。这是一个被编译为数据库引擎可以执行的步骤的查询。通常,在这一点上,它会查看哪些索引可供使用,但在这一点上,参数值还未知,因此查询优化程序无法查看名称上的索引是否有用

他的回答中的变通方法是有效的,但我想再提供一个:


选项(重新编译)
添加到查询中。这将强制每次重新编译查询执行计划,并且每次参数值都是已知的,并允许优化程序针对这些特定参数值进行优化。它通常比完全动态SQL效率稍低,因为动态SQL允许缓存每个可能语句的执行计划,但它可能比您现在拥有的更好,并且比其他选项更易于维护。

Gordon Linoff说得对,这会阻止使用索引,但在此基础上再作进一步阐述:

当SQL Server准备执行查询时,它会生成一个执行计划。这是一个被编译为数据库引擎可以执行的步骤的查询。通常,在这一点上,它会查看哪些索引可供使用,但在这一点上,参数值还未知,因此查询优化程序无法查看名称上的索引是否有用

他的回答中的变通方法是有效的,但我想再提供一个:


选项(重新编译)
添加到查询中。这将强制每次重新编译查询执行计划,并且每次参数值都是已知的,并允许优化程序针对这些特定参数值进行优化。它通常比完全动态SQL效率稍低,因为动态SQL允许缓存每个可能的语句的执行计划,但它可能比您现在拥有的更好,并且比其他选项更易于维护。

您可能会读到关于sargable的内容……这可能不是您想要的。我们将首先获取非@so=1的所有行。@DuduMarkovitz您误读了查询
@所以
是一个参数,它是常量,不依赖于行。我的错,不对焦。你可能会读到sargable…这可能不是你想要的。我们将首先获取非@so=1的所有行。@DuduMarkovitz您误读了查询
@所以
是一个参数,它是常量,不依赖于行。我的问题是,没有焦点。谢谢:)你能解释一下为什么它不能在第二种情况下使用索引吗?它仍然在索引列上排序,但我认为SQL server由于某种原因无法解决这个问题?@DavidKirkpatrick。索引是脆弱的东西。大多数函数或逻辑的使用都会阻止它们被使用。谢谢:)你能解释一下为什么在第二种情况下不能使用索引吗?它仍然在索引列上排序,但我认为SQL server由于某种原因无法解决这个问题?@DavidKirkpatrick。索引是脆弱的东西。大多数函数或逻辑的使用都会阻止它们被使用。啊,这是有道理的-我没有想到当参数已知时查询不会被重新计算。我使用了这个选项(至少目前是这样),因为它是在现有存储过程上实现的最简单的方法。啊,这是有道理的——我没有想到,当参数已知时,查询不会被重新计算。我使用了这个选项(至少目前是这样),因为它是在现有存储过程上实现的最简单的方法。