【问题标题】:How to filter date ranges with filter string (SQL Server)如何使用过滤器字符串过滤日期范围(SQL Server)
【发布时间】:2020-11-20 15:51:27
【问题描述】:

我正在尝试根据 SQL Server 中的输入字符串过滤日期范围。

-- 'All Years', '30', '60', '90', '2019', '2020', etc.
declare @dateRange nvarchar(20) = '90' 

select 
[date],
[data]
from someTable
where (
  @dateRange = case @dateRange
  when 30 then date >= getDate() - 30
  when 60 then date >= getDate() - 60
  when 90 then date >= getDate() - 90
  when 'All Years' then date
  else datePart('year', date) = @dateRange
  end
)

如何使用 where 子句来选择日期范围?

【问题讨论】:

  • '2019' 是什么“日期范围”?你把问题复杂化了。有 2 个参数,一个开始日期和一个结束日期,并使用 >= 和 < 逻辑。
  • < 100 是日期范围,> 2000 是年份范围
  • 2000 不是一个范围,而是一个数字。与 100 一样。范围类似于 30-60,或者日期为 2020-01-01 到 2020-06-30。
  • 它们是隐含的范围...过去 30 天、2019 年全年等

标签: sql sql-server date where-clause


【解决方案1】:

我建议使用布尔逻辑而不是 case 表达式。这为您提供了更细粒度的控制 - 尽管逻辑仍然很麻烦,并且可以通过采用另一种方法来简化。

where 
    (
        @dateRage in ('30', '60', '90') 
        and date >= getDate - try_cast(@dateRange as int)
    )
    or @dateRange = 'All Years'
    or (
        @dateRage not in ('30', '60', '90', 'All Years')
        and datePart('year', date) = @dateRange
    )
)

由于年份永远不会匹配'30'、'60'、'90' 或'All Years',所以最后一个表达式可能可以简化:

where 
    (
        @dateRage in ('30', '60', '90') 
        and date >= getDate - try_cast(@dateRange as int)
    )
    or @dateRange = 'All Years'
    or datePart('year', date) = @dateRange
)

编辑 - 来自 cmets:

2000 是年份范围

这让事情变得更简单了:

where 
    @dateRange = 'All Years'
    or (
        try_cast(@dateRage as int) < 100 
        and date >= getDate - try_cast(@dateRange as int)
    )
    or (
        try_cast(@dateRage as int) > 2000 
        and datePart('year', date) = @dateRange
    )

【讨论】:

  • 当@dateRange nvarchar(20) = 'All Years'时得到Conversion failed when converting the nvarchar value 'All Years' to data type int.。
  • and datePart('year', date) = @dateRange 在 SARGability 方面表现不佳,请注意。
  • @Matthew:是的,当然。我们需要try_cast()。固定
【解决方案2】:

您将问题过于复杂化了。不要使用单个参数来完成应为 2 的工作。使用开始日期和结束日期,然后如果您想要“所有内容”,则为两者传递 NULL:

DECLARE @Startdate date,
        @EndDate date;

--SET @StartDate = ...
--SET @EndDate = ...        

SELECT [date],
       [data]
FROM someTable
WHERE ([date] >= @StartDate AND [date] < @EndDate)
   OR (@StartDate IS NULL AND @EndDate IS NULL)
OPTION (RECOMPILE);   

重新编译是因为查询的包罗万象的部分可能会导致查询计划缓存错误,因此最好每次都重新生成它。

如果由于某种未知的原因,你必须传递字符串(我真的建议你不要,并修复设计),然后使用一些CASE 表达式来设置开始和结束日期:

DECLARE @Param varchar(15);

DECLARE @Startdate date,
        @EndDate date;

SELECT @StartDate = CASE WHEN TRY_CONVERT(int,@Param) < 1000 THEN DATEADD(DAY, TRY_CONVERT(int,@Param) * -1, GETDATE())
                         WHEN TRY_CONVERT(int,@Param) > 1000 THEN DATEFROMPARTS(TRY_CONVERT(int,@Param),1,1)
                    END,
       @EndDate = CASE WHEN TRY_CONVERT(int,@Param) < 1000 THEN DATEADD(DAY,1,GETDATE())
                       WHEN TRY_CONVERT(int,@Param) > 1000 THEN DATEFROMPARTS(TRY_CONVERT(int,@Param)+1,1,1)
                  END;      

如您所见,逻辑真的很奇怪,TRY_CONVERTs 的东西真的很混,所以这不是建议的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-14
    • 2018-05-16
    • 1970-01-01
    • 2014-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多