Sql server SQL Server:如何选择日期范围内的所有日期,即使某些日期内不存在数据

Sql server SQL Server:如何选择日期范围内的所有日期,即使某些日期内不存在数据,sql-server,tsql,Sql Server,Tsql,我有一个应用程序需要显示过去30天的活动条形图。该图表需要显示所有日期,即使当天没有活动 例如: DATE COUNT ================== 1/1/2011 5 1/2/2011 3 1/3/2011 0 1/4/2011 4 1/5/2011 0 etc.... 我可以在查询后进行后期处理,以找出缺少的日期并添加它们,但我想知道是否有更简单的方法在SQL Server中执行此操作。非常感谢定义一个包含日期的静态表,或者动态创建一个temp

我有一个应用程序需要显示过去30天的活动条形图。该图表需要显示所有日期,即使当天没有活动

例如:

DATE       COUNT
==================
1/1/2011   5 
1/2/2011   3 
1/3/2011   0
1/4/2011   4
1/5/2011   0
etc....

我可以在查询后进行后期处理,以找出缺少的日期并添加它们,但我想知道是否有更简单的方法在SQL Server中执行此操作。非常感谢

定义一个包含日期的静态表,或者动态创建一个temp table\table变量来存储您正在使用的活动表中的最小和最大日期之间的每个日期

在两个表之间使用外部联接,以确保dates表中的每个日期都反映在输出中

如果使用静态日期表,您可能希望将输出的日期范围限制为图形中所需的范围。

并像以下那样使用:

declare @DataTable table (DateColumn datetime)
insert @DataTable values ('2011-01-09')
insert @DataTable values ('2011-01-10')
insert @DataTable values ('2011-01-10')
insert @DataTable values ('2011-01-11')
insert @DataTable values ('2011-01-11')
insert @DataTable values ('2011-01-11')

declare @StartDate  datetime
SET @StartDate='1/1/2011'

select
    @StartDate+Number,SUM(CASE WHEN DateColumn IS NULL THEN 0 ELSE 1 END)
    FROM Numbers
        LEFT OUTER JOIN @DataTable ON DateColumn=@StartDate+Number
    WHERE Number>=1 AND Number<=15
    GROUP BY @StartDate+Number
也许是这样的: 创建DaysTable,计算30天。 和包含day列和count列的DataTable。 然后离开,加入他们

WITH    DaysTable (name) AS (
        SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 -- .. And so on to 30
    ),
    DataTable (name, value) AS (        
        SELECT  DATEPART(DAY, [Date]), [Count]
        FROM    YourExampleTable
        WHERE   [Date] < DATEADD (day , -30 , getdate())
    )
SELECT  DaysTable.name, DataTable.value
FROM    DaysTable LEFT JOIN
        DataTable ON DaysTable.name = DataTable.name
ORDER BY DaysTable.name
使用CTE:

WITH DateTable
AS
(
    SELECT CAST('20110101' AS Date) AS [DATE]
    UNION ALL
    SELECT DATEADD(dd, 1, [DATE])
    FROM DateTable
    WHERE DATEADD(dd, 1, [DATE]) < cast('20110201' as Date)
)
SELECT dt.[DATE], ISNULL(md.[COUNT], 0) as [COUNT]
FROM [DateTable] dt
LEFT JOIN [MyData] md
ON md.[DATE] = dt.[DATE]

这是假设一切都是日期;如果是DateTime,则必须使用DATEADDdd,0,DATEDIFFdd,0,[DATE]进行截断。

您可以使用递归CTE构建30天的列表,然后将其加入到数据中

--test
select cast('05 jan 2011' as datetime) as DT, 1 as val into #t
union all select CAST('05 jan 2011' as datetime), 1 
union all select CAST('29 jan 2011' as datetime), 1 

declare @start datetime = '01 jan 2011'
declare @end   datetime = dateadd(day, 29, @start)

;with amonth(day) as
(
    select @start as day
        union all
    select day + 1
        from amonth
        where day < @end
)
select amonth.day, count(val)
    from amonth 
    left join #t on #t.DT = amonth.day
group by amonth.day


>>

2011-01-04 00:00:00.000 0
2011-01-05 00:00:00.000 2
2011-01-06 00:00:00.000 0
2011-01-07 00:00:00.000 0
2011-01-08 00:00:00.000 0
2011-01-09 00:00:00.000 0
...

不使用Transact-SQL:MS SQL 2005-获取一个月中所有天数的列表:

在我的例子中,“20121201”是一个预定义的值


对于那些对递归过敏的人

select SubQ.TheDate
from 
(
    select DATEADD(day, a.a + (10 * b.a) + (100 * c.a), DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0) - 30) AS TheDate
    from 
    (
        (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
        cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
        cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
    ) 
    WHERE a.a + (10 * b.a) + (100 * c.a) < 30
) AS SubQ
ORDER BY TheDate
试试看

DECLARE @currentDate DATETIME = CONVERT(DATE, GetDate())
DECLARE @startDate   DATETIME = DATEADD(DAY, -DAY(@currentDate)+1, @currentDate)

;WITH fnDateNow(DayOfDate) AS
(
    SELECT @startDate AS DayOfDate
        UNION ALL
    SELECT DayOfDate + 1 FROM fnDateNow WHERE DayOfDate < @currentDate
) SELECT fnDateNow.DayOfDate FROM fnDateNow

我的场景比OP示例要复杂一点,所以我想我应该与大家分享,以帮助其他有类似问题的人。我需要按执行日期对销售订单进行分组,而订单是使用datetime存储的

因此,在days查找表中,我无法将时间存储为'00:00:00.000'并获取任何匹配项。因此,我将其存储为字符串,并尝试直接连接转换后的值

这不会返回任何零行,解决方案是执行子查询,返回已转换为字符串的日期

示例代码如下:

declare @startDate datetime = convert(datetime,'09/02/2016')
declare @curDate datetime = @startDate
declare @endDate datetime = convert(datetime,'09/09/2016')
declare @dtFormat int = 102;
DECLARE @null_Date varchar(24) = '1970-01-01 00:00:00.000'

/* Initialize #days table */
select CONVERT(VARCHAR(24),@curDate, @dtFormat) as [Period] into #days

/* Populate dates into #days table */
while (@curDate < @endDate )
begin
    set @curDate = dateadd(d, 1, @curDate)
    insert into #days values (CONVERT(VARCHAR(24),@curDate, @dtFormat))
end

/* Outer aggregation query to group by order numbers */
select [Period], count(c)-case when sum(c)=0 then 1 else 0 end as [Orders],
sum(c) as [Lines] from
(
    /* Inner aggregation query to sum by order lines */ 
    select
        [Period], sol.t_orno, count(*)-1 as c   
        from (
            /* Inner query against source table with date converted */
            select convert(varchar(24),t_dldt, @dtFormat) as [shipdt], t_orno
                from salesorderlines where t_dldt > @startDate
        ) sol
        right join #days on shipdt = #days.[Period]     
        group by [Period], sol.t_orno
) as t
group by Period
order by Period desc

drop table #days

@Alex K.的答案是完全正确的,但它不适用于不支持递归公共表表达式的版本,如我正在使用的版本。在这种情况下,以下内容将完成此工作

DECLARE @StartDate datetime = '2015-01-01'
DECLARE @EndDate datetime = SYSDATETIME()

;WITH days AS
(
  SELECT DATEADD(DAY, n, DATEADD(DAY, DATEDIFF(DAY, 0, @StartDate), 0)) as d
    FROM ( SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
            n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
           FROM sys.all_objects ORDER BY [object_id] ) AS n
)
select days.d, count(t.val)
    FROM days LEFT OUTER JOIN yourTable as t
    ON t.dateColumn >= days.d AND t.dateColumn < DATEADD(DAY, 1, days.d)
GROUP BY days.d
ORDER BY days.d;
声明@StartDate DATE='20110101',@NumberOfYears INT=1; 声明@CutoffDate DATE=DATEADDYEAR、@NumberOfYears、@StartDate; 创建工作台日历 [日期]日期 ; 插入日历[日期] 选择d 从…起 选择d=DATEADDDAY,rn-1,@StartDate 从…起 选择“2011-01-01”、“2011-12-31”这两个日期 rn=按s1排序的行号。[对象id] 从sys.all_对象作为s1 将所有对象交叉连接为s2 按s1排序。[对象\u id] AS x 如y; 创建测试日期表 插入测试值“2011年1月1日” 插入测试值“2011年1月1日” 插入测试值“2011年1月1日” 插入测试值“2011年1月1日” 插入测试值“2011年1月1日” 插入测试值“2011年1月2日” 插入测试值“2011年1月2日” 插入测试值“2011年1月2日” 插入测试值“2011年1月4日” 插入测试值“2011年1月4日” 插入测试值“2011年1月4日” 插入测试值“2011年1月4日”
选择c.date作为日期,countt.a作为日历中的计数c.date=t.a按c.date分组c.date递归CTE最多工作80年,这已经足够了:

DECLARE @dStart DATE,
        @dEnd DATE
SET @dStart = GETDATE ()
SET @dEnd = DATEADD (YEAR, 80, @dStart)
;WITH CTE AS
(
    SELECT @dStart AS dDay
    UNION ALL
    SELECT DATEADD (DAY, 1, dDay)
    FROM CTE
    WHERE dDay < @dEnd
)
SELECT * FROM CTE
OPTION (MaxRecursion 32767)

提示:数字又称日期表如果天数大于100,则此项不起作用。我得到的错误最大递归深度是100。是的,答案回答了问题,但可能会导致错误。那么,您的问题与OP中的问题不同,OP中的问题有一个绝对30项窗口,此解决方案适用于该窗口,不会出错。如果您希望增加递归限制,只需告诉它这样做;选项maxrecursion 100
Period      Orders  Lines
2016.09.09  388     422
2016.09.08  169     229
2016.09.07  1       1
2016.09.06  0       0
2016.09.05  0       0
2016.09.04  165     241
2016.09.03  0       0
2016.09.02  0       0
DECLARE @StartDate datetime = '2015-01-01'
DECLARE @EndDate datetime = SYSDATETIME()

;WITH days AS
(
  SELECT DATEADD(DAY, n, DATEADD(DAY, DATEDIFF(DAY, 0, @StartDate), 0)) as d
    FROM ( SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1)
            n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
           FROM sys.all_objects ORDER BY [object_id] ) AS n
)
select days.d, count(t.val)
    FROM days LEFT OUTER JOIN yourTable as t
    ON t.dateColumn >= days.d AND t.dateColumn < DATEADD(DAY, 1, days.d)
GROUP BY days.d
ORDER BY days.d;
DECLARE @dStart DATE,
        @dEnd DATE
SET @dStart = GETDATE ()
SET @dEnd = DATEADD (YEAR, 80, @dStart)
;WITH CTE AS
(
    SELECT @dStart AS dDay
    UNION ALL
    SELECT DATEADD (DAY, 1, dDay)
    FROM CTE
    WHERE dDay < @dEnd
)
SELECT * FROM CTE
OPTION (MaxRecursion 32767)