sql - T-SQL 条件排序依据

标签 sql sql-server tsql

我正在尝试编写一个存储过程,它返回一个对象列表,其中的排序顺序和排序方向由用户选择并作为 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 的另一个优点是,尽管有很多关于它的散布恐惧的说法:您可以获得每种排序变体的最佳计划,而不是一个针对您碰巧使用的任何排序变体进行优化的单一计划第一的。在我最近进行的一次性能比较中,它的表现也普遍最佳:

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

关于sql - T-SQL 条件排序依据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15621609/

相关文章:

sql - 在第一个 null 之前查找行

sql - 证明SQL查询等效

sql - 运行总计列的复杂 SQL 查询

SQLXML 没有 XML 编码?

sql - 查找上周日

sql - sql查询找到重复的记录

mysql - 有没有一种方法可以使用单个 MySQL 查询来合并两个表,并覆盖和相乘结果

sql - 创建一个为连接 session 的长度声明的变量?

sql - 在没有分组依据的情况下获取每天/每小时的平均值

sql-server - TSQL - FROM 子查询中的 TOP X?