【问题标题】:parameter based date range and category - recursive CTE assistance using daterange function基于参数的日期范围和类别 - 使用日期范围函数的递归 CTE 辅助
【发布时间】:2015-03-16 06:58:22
【问题描述】:

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

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

提前致谢!

【问题讨论】:

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


【解决方案1】:

我假设无论用户输入什么,您都希望查看整个月

因为我仍然想要一个有效的日期,而且无论如何我都会忽略这一天,我将使用 1 号,因为每个月都有一个 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-04
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 2018-05-25
    • 1970-01-01
    相关资源
    最近更新 更多