我正在尝试编写一个存储过程,它返回一个对象列表,其中的排序顺序和排序方向由用户选择并作为 SQL 参数传入。
假设我有一个产品表,其中包含以下列:product_id(int)、name(varchar)、value(int)、created_date(datetime) 和参数@sortDir和@sortOrder
我想做类似的事情
select *
from Product
if (@sortOrder = 'name' and @sortDir = 'asc')
then order by name asc
if (@sortOrder = 'created_date' and @sortDir = 'asc')
then order by created_date asc
if (@sortOrder = 'name' and @sortDir = 'desc')
then order by name desc
if (@sortOrder = 'created_date' and @sortDir = 'desc')
then order by created_date desc
我尝试使用 case 语句来执行此操作,但由于数据类型不同而遇到问题。有人有什么建议吗?
最佳答案
CASE
是一个返回值的表达式。它不像 IF
那样用于流控制。并且您不能在查询中使用 IF
。
不幸的是,CASE
表达式存在一些限制,使得执行您想要的操作变得很麻烦。例如,CASE
表达式中的所有分支必须返回相同的类型,或者可以隐式转换为相同的类型。我不会尝试使用字符串和日期。您也不能使用 CASE
指定排序方向。
SELECT column_list_please
FROM dbo.Product -- dbo prefix please
ORDER BY
CASE WHEN @sortDir = 'asc' AND @sortOrder = 'name' THEN name END,
CASE WHEN @sortDir = 'asc' AND @sortOrder = 'created_date' THEN created_date END,
CASE WHEN @sortDir = 'desc' AND @sortOrder = 'name' THEN name END DESC,
CASE WHEN @sortDir = 'desc' AND @sortOrder = 'created_date' THEN created_date END DESC;
一个可以说更简单的解决方案(特别是当这变得更复杂时)是使用动态 SQL。要阻止 SQL 注入(inject),您可以测试这些值:
IF @sortDir NOT IN ('asc', 'desc')
OR @sortOrder NOT IN ('name', 'created_date')
BEGIN
RAISERROR('Invalid params', 11, 1);
RETURN;
END
DECLARE @sql NVARCHAR(MAX) = N'SELECT column_list_please
FROM dbo.Product ORDER BY ' + @sortOrder + ' ' + @sortDir;
EXEC sp_executesql @sql;
动态 SQL 的另一个优点是,尽管有很多关于它的散布恐惧的说法:您可以获得每种排序变体的最佳计划,而不是一个针对您碰巧使用的任何排序变体进行优化的单一计划第一的。在我最近进行的一次性能比较中,它的表现也普遍最佳:
关于sql - T-SQL 条件排序依据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15621609/