Sql中的多个日期范围选择查询

时间:2017-01-02 13:08:11

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

我目前正在使用Sql Server语言编写C#,尝试设计一个查询,我必须选择四分之一(Q1,Q2,Q3,Q4)。

我的问题是,假设前端的用户只选择了Q1,那么我可以将查询设置为Q1季度的日期范围,如果用户选择Q1,那么我仍然可以设置日期范围对于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存储过程,然后准备/执行它。 但我不想使用临时表来存储日期范围的查询数据,然后将结果联合起来。因为我也有不同的参数。

感谢。

2 个答案:

答案 0 :(得分:3)

您不需要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有时间成分,这也能正常运行。

答案 1 :(得分:0)

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

-- 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(删除了注释,稍微重新格式化)。

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

拆分字符串参考: