【问题标题】:Multiple Dates Ranges in Sql Select QuerySql Select 查询中的多个日期范围
【发布时间】:2017-01-02 13:08:10
【问题描述】:

我目前正在使用 Sql Server 语言开发 C#,尝试设计一个查询,我必须选择季度(Q1、Q2、Q3、Q4)。

我的问题是,假设如果前端用户只选择 Q1,那么我可以将查询设置为 Q1 季度的日期范围,如果用户选择 Q1 以及 Q2,那么我仍然可以设置日期范围对于 Q1 和 Q2,因为它们属于序列日期范围。

但如果用户选择 Q1 和 Q3,那么我如何准备日期范围查询(在存储过程或简单选择查询中)并为此执行。

我试过这个但没用。

where CDate in(cdate between '2015-05-20' and '2015-06-01' 
  and CDate between '2016-06-03' and '2016-06-04'

select * from MM  where @innerstring

我想将@innerstring 作为参数发送到 sql 存储过程,然后准备/执行它。 但我不想使用临时表来存储单独日期范围的查询数据,然后合并该结果。因为我也有不同的参数。

谢谢。

【问题讨论】:

  • 为什么是 MySQL 标签?
  • 您必须添加示例数据和预期结果,我们不完全了解您想对 quaters 或日期做什么。

标签: sql sql-server tsql c#-4.0


【解决方案1】:

您不需要in。这应该有效:

where (cdate between '2015-05-20' and '2015-06-01' or
       cdate between '2016-06-03' and '2016-06-04'
      )

请注意,带有日期的 between 可能是危险的/处于中间位置的,正如 Aaron Bertrand 在有趣的 blog 中解释的那样。我会去:

where (cdate >= '2015-05-20' and cdate < '2015-06-02' or
       cdate >= '2016-06-03' and cdate < '2016-06-05'
      )

即使cdate 有时间组件,这也可以安全工作。

【讨论】:

  • 不,它只是选择之后第一个日期范围的记录,或者什么都不做。我想包含两个日期范围的结果,我的意思是从 cdate >= '2015-05-20' 和 cdate = '2016- 的结果06-03' 和 cdate
  • 好的,谢谢。我现在工作了。但是如何在 SQL Query 中准备它,我随机选择 Q1 到 Q4,那么如何根据我的情况准备查询。查询必须在 sql 存储过程中。
  • 戈登·林诺夫,关于上面的任何想法。
  • 我会添加一些括号以提高可读性:where ((cdate &gt;= '2015-05-20' and cdate &lt; '2015-06-02') or (cdate &gt;= '2016-06-03' and cdate &lt; '2016-06-05') )
  • 如何使存储过程中的整个 where 子句替换为字符串值。就像上面我刚刚编辑的一样。
【解决方案2】:

如果您想根据季度选择日期范围,您可以使用日历表来实现这一点。以下示例使用来自Creating a date dimension or calendar table in SQL Server - Aaron Bertrand 的 sn-ps。

-- if regional settings are interfering with interpretation of dates/literals:
/*
set datefirst 7;
set dateformat mdy;
set language us_english;
--*/
if object_id('dbo.Calendar_Example') is not null drop table dbo.Calendar_Example;
create table dbo.Calendar_Example (
    [Date]         date     not null
  , [Year]         smallint not null
  , [Day]          tinyint  not null
  , [Month]        tinyint  not null
  , [Quarter]      tinyint  not null
  , [YearQuarter]  char(7)  not null /* yyyy-qq */
  , constraint pk_Calendar_Quarters primary key clustered (date)
  );

declare @FromDate date = '20160101';
declare @ThruDate date = '20201231';

with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, d as (
  select DateValue=convert(date,dateadd(day
      , row_number() over (order by (select 1)) -1, @fromdate))
    from         n as deka
      cross join n as hecto
      cross join n as kilo     /* 2.73 years */
      cross join n as [10k]    /* 27.3 years */
      --cross join n as [100k] /* 273  years */
      --cross join n as mega
)
insert into dbo.Calendar_Example 
    ([Date], [Year], [Day], [Month], [Quarter], [YearQuarter])
  select top (datediff(day, @FromDate, @ThruDate)+1) 
      [Date]        = DateValue
    , [Year]        = convert(smallint,datepart(year,DateValue))
    , [Day]         = convert(tinyint,datepart(day,DateValue)) 
    , [Month]       = convert(tinyint,datepart(month,DateValue)) 
    , [Quarter]     = convert(tinyint,datepart(quarter,DateValue)) 
    , [YearQuarter] = convert(char(7) ,convert(char(4), datepart(year,DateValue))
                      +'-Q'+convert(char(1), datepart(quarter,DateValue)))
    from d
    order by DateValue;

日历和数字表参考:


从那里,您可以使用 表值参数 (TVP) 传递一组季度以包含在选择中。示例:

create type dbo.YearQuarter_udt as table (
    YearQuarter char(7) not null
  , primary key (YearQuarter)
    );
go

create procedure dbo.Select_Using_YearQuarter_udt (
    @YearQuarter dbo.YearQuarter_udt readonly
) as
begin;
  set nocount on;
  set xact_abort on;
  with c as (
    select ce.[Date] 
      from dbo.Calendar_Example ce  
        inner join @YearQuarter yq on yq.YearQuarter = ce.YearQuarter
      )
  select *
    from MM
      inner join c  on mm.CDate = c.Date;
end;
go

如果你想传递一系列日期范围,你可以这样做:

create type dbo.DateRange_udt as table (
    FromDate date not null
  , ThruDate date not null
  primary key (FromDate,ThruDate)
    );
go

create procedure dbo.Select_Using_DateRange_udt (
    @DateRange dbo.DateRange_udt readonly
) as
begin;
  set nocount on;
  set xact_abort on;
  select *
    from MM
      inner join @DateRange dr on mm.CDate >= dr.FromDate 
                              and mm.CDate <= dr.ThruDate
end;
go

表值参数参考:


TVP 的替代方案,您可以拆分 @innerstring 变量。

create procedure dbo.Select_Using_YearQuarter_Delimited (
    @DelimitedString nvarchar(256)
) as
begin;
  set nocount on;
  set xact_abort on;
  with c as (
    select ce.[Date] 
      from dbo.Calendar_Example ce  
        inner join dbo.DelimitedSplitN4k(@DelimitedString,';') as d 
          on convert(char(7),d.Item) = ce.YearQuarter
      )
  select *
    from MM
      inner join c  on mm.CDate = c.Date;
end;
go

以上示例假定分隔符为 ';',并使用 Jeff Moden's DelimitedSplitN4k(删除了 cmets,稍微重新格式化)。

create function dbo.DelimitedSplitN4K (
    @pString nvarchar(4000)
  , @pDelimiter nchar(1)
  )
returns table with schemabinding as
return
  with e1(n) as (
    select 1 union all select 1 union all select 1 union all 
    select 1 union all select 1 union all select 1 union all 
    select 1 union all select 1 union all select 1 union all select 1
  )
  , e2(n) as (select 1 from e1 a, e1 b)
  , e4(n) as (select 1 from e2 a, e2 b)
  , cteTally(n) as (select top (isnull(datalength(@pString)/2,0))
      row_number() over (order by (select null)) from e4)
  , cteStart(n1) as (select 1 union all 
      select t.n+1 from cteTally t where substring(@pString,t.n,1) = @pDelimiter)
  , cteLen(n1,l1) as(select s.n1
  ,   isnull(nullif(charindex(@pDelimiter,@pString,s.n1),0)-s.n1,4000)
    from cteStart s
  )
 select ItemNumber = row_number() over(order by l.n1)
      , Item       = substring(@pString, l.n1, l.l1)
   from cteLen l;
go

拆分字符串参考:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-16
    • 1970-01-01
    • 1970-01-01
    • 2012-01-11
    • 1970-01-01
    相关资源
    最近更新 更多