54

I am trying to write a stored procedure that returns a list of object with the sort order and sort direction selected by the user and passed in as sql parameters.

Lets say I have a table of products with the following columns: product_id(int), name(varchar), value(int), created_date(datetime) and parameters @sortDir and @sortOrder

I want to do something like

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

I tried do it with case statements but was having problems since the data types were different. Anyone got any suggestions?

4

4 回答 4

84

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 注入,您可以测试这些值:

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 的另一个优点,尽管它散布了所有的恐惧:你可以为每个排序变体获得最佳计划,而不是一个单一的计划,将优化你碰巧首先使用的任何排序变体。在我最近进行的一次性能比较中,它也普遍表现最好:

http://sqlperformance.com/conditional-order-by

于 2013-03-25T18:07:07.617 回答
20

你需要一个 case 语句,尽管我会使用多个 case 语句:

order by (case when @sortOrder = 'name' and @sortDir = 'asc' then name end)  asc,
         (case when @sortOrder = 'name' and @sortDir = 'desc' then name end) desc,
         (case when @sortOrder = 'created_date' and @sortDir = 'asc' then created_date end) asc,
         (case when @sortOrder = 'created_date' and @sortDir = 'desc' then created_date end) desc

拥有四个不同的子句消除了类型之间的转换问题。

于 2013-03-25T18:08:13.583 回答
4

有多种方法可以做到这一点。一种方法是:

SELECT *
FROM
(
  SELECT
  ROW_NUMBER() OVER ( ORDER BY
  CASE WHEN @sortOrder = 'name' and @sortDir = 'asc' then name
  END ASC,
  CASE WHEN @sortOrder = 'name' and @sortDir = 'desc' THEN name
  END DESC,
  CASE WHEN i(@sortOrder = 'created_date' and @sortDir = 'asc' THEN created_date
  END ASC,
  CASE WHEN i(@sortOrder = 'created_date' and @sortDir = 'desc' THEN created_date
  END ASC) RowNum
  *
)
order by 
RowNum

您也可以使用动态 sql 来执行此操作。

于 2013-03-25T18:10:37.850 回答
1
declare @str varchar(max)
set @str = 'select * from Product order by ' + @sortOrder + ' ' + @sortDir
exec(@str)
于 2013-03-25T18:08:16.570 回答