如何将@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)
答案 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工作负载进行优化”设置。这可以防止此查询的任何变体的计划在被使用两次之前完全缓存。