ORDER BY 和 CASE

ORDER BY and CASE

我在 SQL 中有以下脚本:

ALTER PROC [dbo].[getRequests]
@SortBy             VARCHAR(50) = 'Date'
AS
SELECT  [ReqDate], [RequestorOrg], [RequestCategory],
        [ReqDescription], [OrgCountVote]
ORDER BY 
        CASE WHEN @SortBy = 'Date (oldest first)'   THEN [ReqDate]          END ASC, [OrgCountVote] DESC,
        CASE WHEN @SortBy = 'Date (newest first)'   THEN [ReqDate]          END DESC, [OrgCountVote] ,
        CASE WHEN @SortBy = 'Number of Votes'       THEN [OrgCountVote]     END DESC, reqdate,
        CASE WHEN @SortBy = 'Organisation'          THEN [RequestorOrg]     END,
        CASE WHEN @SortBy = 'Category'              THEN [RequestCategory]  END;

ORDER BY 中的前两个案例有多个要排序的列。

当我尝试保存脚本时,出现以下消息:

Msg 169, Level 15, State 1, Procedure getRequests, Line 8
A column has been specified more than once in the order by list. Columns in the order by list must be unique.

如何确保 列是唯一的

您应该将逻辑从使用复杂的 ORDER BY 列列表更改为使用 IF 子句并指定多个 select 语句和 ORDER BYs。

喜欢:

If @SortBy = 'Date....' then
Begin
    -- SQL with the single order by group...
End
Else if @SortBy =... Then...
Begin

End

如果您必须使用案例,这可能会给您类似的结果:

ALTER PROC [dbo].[getRequests]
@SortBy             VARCHAR(50) = 'Date'
AS
SELECT  [ReqDate], [RequestorOrg], [RequestCategory],
    [ReqDescription], [OrgCountVote]
FROM requests
ORDER BY 
CASE 
WHEN @SortBy = 'Date (oldest first)' THEN Datediff(day, '2010-1-1', [ReqDate]) 
WHEN @SortBy = 'Date (newest first)'   THEN [ReqDate]          
WHEN @SortBy = 'Number of Votes'       THEN [RequestorOrg]     
WHEN @SortBy = 'Category'              THEN [RequestCategory]  
END DESC, 
CASE WHEN @SortBy = 'Date (newest first)'    THEN OrgCountVote
WHEN @SortBy = 'Number of Votes'       THEN [OrgCountVote] 
ELSE '' END

同样,您可能不想这样做。性能可能会受到影响。

我建议你使用这种类型的存储过程:

ALTER PROC [dbo].[getRequests]
@SortBy             VARCHAR(50) = 'Date'
AS
BEGIN
DECLARE @sql varchar(max) 

SET @sql = 'SELECT  [ReqDate], [RequestorOrg], [RequestCategory], [ReqDescription], [OrgCountVote] ' +
           'ORDER BY ' +
                CASE @SortBy
                    WHEN 'Date (oldest first)' THEN '[ReqDate] ASC, [OrgCountVote] DESC'
                    WHEN 'Date (newest first)' THEN '[ReqDate] DESC, [OrgCountVote] '
                    WHEN 'Number of Votes'     THEN '[OrgCountVote] DESC, reqdate'
                    WHEN 'Organisation'        THEN '[RequestorOrg]'
                    WHEN 'Category'            THEN '[RequestCategory]'
                END
EXEC(@sql)
END

问题出在 OrgCountVote 字段。 CASE 语句中包含的字段很好......至少,它在我的测试系统(Sql Server 2008 R2)上运行良好。即便如此,我还是建议为每个 case 条件添加一个默认的 ELSE 子句,您还应该知道这可能会导致一些非常糟糕的查询计划(性能差)。对于这种事情,您将在客户端代码中进行排序时获得 更好的性能。

也就是说,您可以像这样完成您的需要:

ORDER BY
  --first level
    CASE WHEN @SortBy = 'Date (oldest first)' THEN [ReqDate]
         WHEN @SortBy = 'Date (newest first)' THEN CAST(CAST([ReqDate]-'1970-01-01' AS decimal(38,10))*-24*60*60*1000+0.5 as bigint)
         WHEN @SortBy = 'Number of Votes'     THEN [OrgCountVote] * -1 
         WHEN @SortBy = 'Organisation'        THEN [RequestorOrg]
         WHEN @SortBy = 'Category'            THEN [RequestCategory]  END, 
  --second level
    CASE WHEN @SortBy = 'Date (oldest first)' THEN [OrgCountVote] * -1
         WHEN @SortBy = 'Date (newest first)' THEN [OrgCountVote]
         WHEN @SortBy = 'Number of Votes'     THEN reqdate
         ELSE NULL END

我需要解释一下 Date (newest first) 条目。如果你只是做一个简单的 datediff,你最终会溢出整数类型。该长表达式的其余部分只是将日期字段转换为可以乘以 -1 的数字,从而反转排序顺序。归功于此 link 如何做到这一点:

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/51cfea50-f915-4bac-bf7d-d053329086cd/difference-of-dates-in-millisecond?forum=transactsql

如果您不需要毫秒粒度(即:您只存储日期值,时间组件中全为零)或者如果您不关心它不是完美的(记录几乎在同一时间可能会出现问题,也许如果这对您的数据来说是极其罕见的),那么您可以大大简化该表达式并只取自 unix 纪元以来的秒数或分钟数或天数自 Sql 服务器纪元以来。

我想出了一个非常动态的方法来执行 ORDER BY。在我下面的示例中,我可以根据需要添加任意数量的 WHEN,它只会逐步执行它们并选择相关语句。

ORDER BY   
CASE WHEN @SortBy = 'Date (oldest first)'   THEN [ReqDateTime]      END ASC,  
CASE WHEN @SortBy = 'Date (oldest first)'   THEN [OrgCountVote]     END DESC,  
CASE WHEN @SortBy = 'Date (newest first)'   THEN [ReqDateTime]      END DESC,  
CASE WHEN @SortBy = 'Date (newest first)'   THEN [OrgCountVote]     END DESC,  
CASE WHEN @SortBy = 'Number of Votes'       THEN [OrgCountVote]     END DESC,   
CASE WHEN @SortBy = 'Number of Votes'       THEN [ReqDateTime]      END DESC,  
CASE WHEN @SortBy = 'Organisation'          THEN [RequestorOrg]     END,  
CASE WHEN @SortBy = 'Status'                THEN ReqStatus          END,  
CASE WHEN @SortBy = 'Category'              THEN [RequestCategory]  END;  

我不必担心 ELSE,因为参数具有分配给它的默认值,因此将始终具有以下语句之一:

@SortBy             VARCHAR(50) = 'Status'