基于参数的日期范围和类别 - 使用日期范围函数的递归CTE辅助

时间:2015-01-17 22:03:58

标签: sql sql-server-2008 common-table-expression date-range

我想要一个查询来返回不存在的行的值,以帮助制定统计信息查询..我相信最简单的分享样本..这是我的超小样本日期集:

category    date        ID
Swimming    2013-02-09  1
Hiking      2013-05-01  2
Archery     2013-07-03  3
Swimming    2013-08-05  4
Swimming    2013-08-22  5
Archery     2013-09-01  6
Swimming    2013-09-18  7
NULL        2013-09-19  8

我现在的查询:

DECLARE @startdate DATETIME = '04/01/2013'
DECLARE @enddate DATETIME = '10/31/2013'
DECLARE @activitycategory VARCHAR(40) = 'Swimming'

SELECT category AS 'Activity'
    ,CASE WHEN category is null THEN 'No Category' 
        ELSE category
        END as ActivityCategory
    ,DATEPART(yyyy,date) AS 'Year'
    ,DATEPART(mm,date) AS 'Month'
    ,COUNT(ID) AS 'Total Activities'
FROM classes
WHERE date BETWEEN @startdate AND @enddate
AND category IN (@activitycategory)
GROUP BY category
,DATEPART(yyyy,date)
,DATEPART(mm,date)
ORDER BY category
,DATEPART(yyyy,date)
,DATEPART(mm,date)

我的查询目前产生以下结果:

Activity    ActivityCategory    Year    Month   Total Activities
Swimming    Swimming            2013    8       2
Swimming    Swimming            2013    9       1

我想要以下结果:

Activity    ActivityCategory    Year    Month   Total Activities
Swimming    Swimming            2013    4       0
Swimming    Swimming            2013    5       0
Swimming    Swimming            2013    6       0
Swimming    Swimming            2013    7       0
Swimming    Swimming            2013    8       2
Swimming    Swimming            2013    9       1
Swimming    Swimming            2013    10      0

我做了很多研究,似乎我需要使用递归CTE ..我发现了一个“日期范围”功能,我认为显然是必要的(在有一个日历表之外)..我只是无法想象如何编写最终查询..正如您将注意到我的查询需要具有参数...(最终这将是一个SSRS报告)..无论用户选择什么,没有行返回的计数将产生一个如果在参数选择的日期范围内不存在“总活动”,则为“0”..任何帮助表示赞赏..这里是我发现的功能:

CREATE FUNCTION [dbo].[DateRange]
(     
      @Increment              CHAR(1),
      @StartDate              DATETIME,
      @EndDate                DATETIME
)
RETURNS  
@SelectedRange    TABLE 
(IndividualDate DATETIME)
AS 
BEGIN
      ;WITH cteRange (DateRange) AS (
            SELECT @StartDate
            UNION ALL
            SELECT 
                  CASE
                        WHEN @Increment = 'd' THEN DATEADD(dd, 1, DateRange)
                        WHEN @Increment = 'w' THEN DATEADD(ww, 1, DateRange)
                        WHEN @Increment = 'm' THEN DATEADD(mm, 1, DateRange)
                  END
            FROM cteRange
            WHERE DateRange <= 
                  CASE
                        WHEN @Increment = 'd' THEN DATEADD(dd, -1, @EndDate)
                        WHEN @Increment = 'w' THEN DATEADD(ww, -1, @EndDate)
                        WHEN @Increment = 'm' THEN DATEADD(mm, -1, @EndDate)
                  END)

      INSERT INTO @SelectedRange (IndividualDate)
      SELECT DateRange
      FROM cteRange
      OPTION (MAXRECURSION 3660);
      RETURN
END
GO

提前致谢!

1 个答案:

答案 0 :(得分:1)

我将假设无论用户输入什么,您都需要查看整个月

因为我仍然想要一个有效的约会而且我将无视这一天我将使用第一个,因为每个月都有一个月的第一天。

显然,如果您决定使用日期表,则可以将其替换为递归CTE(r_cte)

在您的脚本中

尝试使用IN(@category)作为过滤器表达式, 我刚刚使用了=因为我们没有将列值与可能值列表进行比较

declare @data table (category varchar(10),activity_date datetime, ID int)
insert into @data values ('Swimming','2013-02-09',1)
insert into @data values ('Hiking','2013-05-01',2)
insert into @data values ('Archery','2013-07-03',3)
insert into @data values ('Swimming','2013-08-05',4)
insert into @data values ('Swimming','2013-08-22',5)
insert into @data values ('Archery','2013-09-01',6)
insert into @data values ('Swimming','2013-09-18',7)
insert into @data values (NULL,'2013-09-19',8)

declare @date_start datetime = '20130414'
declare @date_end datetime = '20131031'
declare @activity_category varchar(10) = 'Swimming'

set @date_start = dateadd(day,-(datepart(day,@date_start)-1),@date_start)
set @date_end = dateadd(day,-(datepart(day,@date_end)-1),@date_end)

;with r_cte
as (select @date_start as activity_period
    union all
    select dateadd(month, 1, activity_period)
    from r_cte
    where activity_period < @date_end
    )
select 
    isnull(category,@activity_category) as Activity ,
    isnull(category,@activity_category) as ActivityCategory,
    DATEPART(MONTH,activity_period) as ActivityMonth,
    DATEPART(YEAR,activity_period) as ActivityYear,
    datename(month,activity_period) + ' ' + convert(varchar(4),datepart(year,activity_period)) as ActivityPeriod,
    count(category) as TotalActivities
from r_cte r
    left join 
            (select 
                dateadd(day,-(datepart(day,activity_date)-1),activity_date) as compare_date,
                isnull(category,'No Category') as category
            from @data
            where category = @activity_category) d
        on d.compare_date = r.activity_period
group by category, activity_period
order by r.activity_period