如何传递“Order By”和“Order Type”参数?

时间:2013-10-24 14:37:22

标签: sql-server

如何将@OrderBy@OrderType参数传递给此过程以支持不同的排序选项?我想要完成的是什么,但语法无效:

WITH results AS
(
    SELECT id, title, LastModified, 
      ROW_NUMBER() over (ORDER BY @OrderBy @OrderType) RowNum
----------------------------------^^^^^^^^^^^^^^^^^^^
    FROM dbo.EmploymentOpportunities
    where CompanyId = 148

)
SELECT id, title, LastModified, (select count(*) from results) totalcount
FROM results
where RowNum between 1 and 9
ORDER BY RowNum 
OPTION (Maxdop 8)

1 个答案:

答案 0 :(得分:4)

动态ORDER BY可以是简单的也可以是复杂的,具体取决于所涉及的数据类型。如果所有内容都是DATETIME,那么您可以说:

;WITH ... ( , RowNum = ROW_NUMBER() OVER (ORDER BY 
CASE WHEN @OrderType = 'ASC' THEN
  CASE WHEN @OrderBy = 'LastModified' THEN LastModified
       WHEN @OrderBy = 'DateCreated' THEN DateCreated
  END
END,
CASE WHEN @OrderType = 'DESC' THEN
  CASE WHEN @OrderBy = 'LastModified' THEN LastModified
       WHEN @OrderBy = 'DateCreated' THEN DateCreated
  END
END DESC) FROM ... 
)
SELECT ... ORDER BY RowNum;

如果您有混合数据类型,它会变得复杂得多。由于CASE返回表达式并且数据类型都必须兼容,因此每种数据类型都需要不同的分支。

除了复杂性之外,这不太可能为所有可能的组合产生良好的计划。所以我宁愿做的是使用动态SQL。

DECLARE @sql NVARCHAR(MAX) = N';WITH ... ( , RowNum = ROW_NUMBER() OVER (ORDER BY '
  + @OrderBy + ' ' + @OrderType + ') FROM ... ) SELECT ... 
  WHERE RowNum BETWEEN @s AND @e
  ORDER BY RowNum;';

EXEC sp_executesql @sql, N'@s INT, @e INT', @StartOfRange, @EndOfRange;

现在这使您可以打开SQL注入,因此您应首先验证@OrderBy和@OrderType参数是否包含您期望的值(您可以针对sys.columns检查前者以使您的代码向前兼容,并检查后者是'ASC'或'DESC')。

还有计划缓存膨胀的问题。如果您使用的是SQL Server 2008或更高版本,请翻转“针对ad hoc工作负载进行优化”设置。这可以防止此查询的任何变体的计划在被使用两次之前完全缓存。