我有一个应用需要显示过去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中是否有更简单的方法。非常感谢
答案 0 :(得分:35)
您可以使用递归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
...
答案 1 :(得分:9)
使用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,则必须截断(使用DATEADD(dd, 0, DATEDIFF(dd, 0, [DATE]))
)。
答案 2 :(得分:3)
@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;
答案 3 :(得分:1)
定义包含日期的静态表或动态创建临时表\ table变量,以便在您正在使用的活动表中的最小和最大日期之间存储(并包括)每个日期。
在两个表之间使用外部联接,以确保日期表中的每个日期都反映在输出中。
如果使用静态日期表,您可能希望将输出的日期范围限制为仅在图表中所需的范围。
答案 4 :(得分:1)
没有Transact-SQL:MS SQL 2005 - 获取一个月中所有日子的列表:
在我的情况下,'20121201'是预定义的值。
SELECT TOp (Select Day(DateAdd(day, -Day(DateAdd(month, 1,
'20121201')),
DateAdd(month, 1, '20121201')))) DayDate FROM ( SELECT DATEADD(DAY,ROW_NUMBER() OVER (ORDER BY (SELECT
NULL))-1,'20121201') as DayDate FROM sys.objects s1 CROSS JOIN
sys.objects s2 ) q
答案 5 :(得分:1)
我的情景比OP示例复杂一些,所以我想分享一些帮助其他有类似问题的人。我需要按照日期对销售订单进行分组,而订单则按日期时间存储。
所以在“天”查询表中,我无法真正存储为时间为'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
示例结果:
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
答案 6 :(得分:0)
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
输出:
----------------------- -----------
2011-01-02 00:00:00.000 0
2011-01-03 00:00:00.000 0
2011-01-04 00:00:00.000 0
2011-01-05 00:00:00.000 0
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 1
2011-01-10 00:00:00.000 2
2011-01-11 00:00:00.000 3
2011-01-12 00:00:00.000 0
2011-01-13 00:00:00.000 0
2011-01-14 00:00:00.000 0
2011-01-15 00:00:00.000 0
2011-01-16 00:00:00.000 0
(15 row(s) affected)
答案 7 :(得分:0)
也许是这样的: 创建 DaysTable 计算30天。 DataTable 包含“day”列和“count”列。 然后离开加入他们。
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
答案 8 :(得分:0)
对于那些有递归过敏的人
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
答案 9 :(得分:0)
试试吧。
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
答案 10 :(得分:0)
DECLARE @StartDate DATE = '20110101', @NumberOfYears INT = 1;
DECLARE @CutoffDate DATE = DATEADD(YEAR, @NumberOfYears, @StartDate);
CREATE TABLE Calender
(
[date] DATE
);
INSERT Calender([date])
SELECT d
FROM
(
SELECT d = DATEADD(DAY, rn - 1, @StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, '2011-01-01', '2011-12-31'))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id]
) AS x
) AS y;
create table test(a date)
insert into test values('1/1/2011')
insert into test values('1/1/2011')
insert into test values('1/1/2011')
insert into test values('1/1/2011')
insert into test values('1/1/2011')
insert into test values('1/2/2011')
insert into test values('1/2/2011')
insert into test values('1/2/2011')
insert into test values('1/4/2011')
insert into test values('1/4/2011')
insert into test values('1/4/2011')
insert into test values('1/4/2011')
select c.date as DATE,count(t.a) as COUNT from calender c left join test t on c.date = t.a group by c.date
&#13;
答案 11 :(得分:0)
递归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)