【问题标题】:SQL Server Conditional Order BySQL Server 条件排序方式
【发布时间】:2011-06-29 16:23:00
【问题描述】:

我在 SQL Server 2005 中有一个 SQL 查询,当我包含条件 order by 时该查询会中断。当我删除订单时,查询有效。当我明确地按条件编写顺序(例如,按 p.Description 顺序)时,它可以工作。当我包含条件顺序时,我收到错误,

'Conversion failed when converting character string to smalldatetime data type'

SQL Server 没有告诉我是哪一行代码导致了这个错误。我想知道如何解决这个问题,以便我可以使用条件顺序或排除转换失败的列。

declare @SearchTerm nvarchar(255)
declare @SortBy nvarchar(255)
declare @Months int
declare @VendorID int
declare @ProductID int

set @SearchTerm = 'focus'
set @SortBy = 'product'
set @Months = 3
set @VendorID = null
set @ProductID = null

-- This makes it so the @Month will filter by n number of months ago.
declare @PreviousMonths datetime
if @Months is null
    begin
        set @PreviousMonths = 24
    end
else
    begin
        set @PreviousMonths = DateAdd(month, -@Months, GetDate())
    end

select
    a.dsAlertID as AlertID,
    a.ProductID,
    v.VendorID,
    p.Description as ProductName,
    v.LongName as VendorName,
    a.Introduction,
    a.Writeup,
    a.DateAdded 
from
    ev_ds_Alerts a
left outer join
    tblProducts p on a.ProductID = p.ProductID
left outer join
    tblVendors v on v.VendorID = p.VendorID
where
    ( @SearchTerm is null or ( a.Writeup like '% ' + @SearchTerm + '%' or a.Introduction like '% ' + @SearchTerm + '%') )
    and (( @Months is null ) or ( @Months is not null and a.DateAdded >= @PreviousMonths))
    and (( @VendorID is null ) or ( @VendorID is not null and v.VendorID = @VendorID ))
    and (( @ProductID is null ) or ( @ProductID is not null and p.ProductID = @ProductID ))
order by
    case @SortBy
        when 'product' then p.Description
        when 'vendor' then v.LongName
        else a.DateAdded
    end

-- order by p.Description or v.LongName works when explicitly writing them out!

【问题讨论】:

  • 制作独立的case语句以避免数据类型转换错误

标签: sql sql-server stored-procedures


【解决方案1】:

根据上一个答案,尝试:

order by
    case @SortBy
        when 'product' then p.Description
        when 'vendor' then v.LongName
        else convert(VARCHAR(25),a.DateAdded,20)

这应该会给你你想要的排序,因为它将格式化日期字符串 yyyy-mm-dd hh:mm:ss。

【讨论】:

  • +1 很高兴您记得将日期格式化为 yyyy-mm-dd hh:mm:ss 以保持一致的顺序。
  • 这正是我需要做的。非常感谢!
  • 性能方面,这是一条狗,最好分成 3 个 SELECT 语句,每个语句都依赖于 @SortBy 运行
【解决方案2】:

您可以为每种数据类型使用一种情况:

order by
  case @SortBy
    when 'product' then p.Description
    when 'vendor' then v.LongName
    else ''
  end,
  case @SortBy
    when 'added' then a.DateAdded
    else '1980-01-01'
  end

【讨论】:

  • +1。我没有看到你的答案,我写过类似的东西
【解决方案3】:

ORDER BY 中使用CASE 表达式时,返回的数据类型必须始终相同。

如果不使用动态 SQL 或某种形式的决策逻辑(即:IF)来分解不同的查询,您将无法挑选您想要的内容 - INT、DATETIME、VARCHAR 等。

在此示例中,您可以使用 CAST/CONVERT 将 DATETIME 数据类型更改为适当的 VARCHAR。但除非您知道为什么会发生此问题,否则您将来很可能会再次遇到此问题。

【讨论】:

  • 这是有道理的。使用 case 表达式的适当解决方法是什么?
  • @Halcyon: 要么使用IF 来分解数据类型处理以进行单独的查询,要么使用动态SQL。
  • 很好的解释,但没有解释 OP 如何执行订单。根据@Neil 和@gibeath:铸造。
  • @Adrian:在这个例子中,铸造起作用——这是治疗症状,而不是问题。我提供了两种解决问题的方法——如果 OP 想要更多细节,他们可以专门询问。
  • 这解释了为什么查询不起作用,这是我没有意识到的。 gibeath 提供了一个可行的解决方案。谢谢大家!
【解决方案4】:

要排序的列列表中的 NULL 被忽略,因此您可以按类型将它们分解;

ORDER BY
    CASE 
       WHEN @SortBy = 'product' THEN p.Description 
       WHEN @SortBy = 'vendor' THEN v.LongName 
    END
    ,
    CASE WHEN @SortBy NOT IN ('product', 'vendor') THEN cda.StartDate END   

最后一个有点难看,如果可以的话更好;

 CASE WHEN @SortBy = '' THEN cda.StartDate END    

【讨论】:

  • 我认为您可以从答案中删除第二种情况,只需添加 StartDate。在第一种情况下,使用 else 默认为空格。查看我的代码
  • 没错,不同之处在于总是会按日期(在其他排序之后)应用可能需要也可能不需要的排序
【解决方案5】:

如果您关心性能,您可能需要不同的方法: 1. 将您的选择包含在内联 TVF 中 2. 使用两个不同的 SELECT,这样他们就可以获得两个不同的计划,这可能比您现在获得的通用的一刀切的计划更有效:

IF @SortBy='product' BEGIN
  SELECT AlertID,
(snip)
  FROM MyTvf
  ORDER BY Description ;
  RETURN @@ERROR ;
END 

IF @SortBy='Vendor' BEGIN
  SELECT AlertID,
(snip)
  FROM MyTvf
  ORDER BY LongName ;
  RETURN @@ERROR ;
END 

【讨论】:

  • 这个性能比orderby子句中的case语句要好很多
猜你喜欢
  • 2016-02-05
  • 1970-01-01
  • 2011-01-16
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2011-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多