我有一个表,根据警报ID来存储错误数量。该表看起来像这样:
|----DATE----|---ALARM_ID---|---COUNTER---|
| 2012-01-01 | 1 | 32 |
| 2012-01-01 | 2 | 28 |
| 2012-01-02 | 1 | 12 |
| 2012-01-02 | 2 | 23 |
| 2012-01-03 | 1 | 3 |
| 2012-01-03 | 2 | 9 |
| 2012-01-05 | 1 | 8 |
| 2012-01-05 | 2 | 1 |
| 2012-01-07 | 1 | 102 |
| 2012-01-07 | 2 | 78 |
请注意日期(2012-01-03 - 2012-01-05)与(2012-01-05 - 2012-01-07)之间的差距。在这些日期没有任何数据,因为我的程序正在监视的系统在该日期没有报告任何错误。我正在寻找的是一个SQL SELECT查询,它返回每个日期的错误总数,例如:
|----DATE----|---COUNTER---|
| 2012-01-01 | 60 |
| 2012-01-02 | 35 |
| 2012-01-03 | 12 |
| 2012-01-04 | 0 |
| 2012-01-05 | 9 |
| 2012-01-06 | 0 |
| 2012-01-07 | 180 |
我有一个返回ID的查询,即使它们不存在于表中,如果该ID不存在,仍然以COUNTER值0返回ID。如下:
BEFORE AFTER
|---ID---|---COUNTER---| |---ID---|---COUNTER---|
| 1 | 2 | | 1 | 2 |
| 2 | 6 | | 2 | 6 |
| 3 | 1 | --> | 3 | 1 |
| 5 | 9 | | 4 | 0 |
| 6 | 10 | | 5 | 9 |
| 6 | 10 |
| 7 | 0 |
| 8 | 0 |
查询如下:
select t.num as ID, coalesce(yt.COUNTER, 0)
from all_stats yt right join
( select t1.num + t2.num * 10 + t3.num * 100 + t4.num * 1000 as num
from ( select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0 ) t1 cross join
( select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0 ) t2 cross join
( select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0 ) t3 cross join
( select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0 ) t4 )
t on yt.ID = t.num
where (t.num between (select min(ID) from all_stats) and (select max(ID) from all_stats)) order by ID
我无法弄清楚如何在关于日期时更改此查询。有人可以帮我解决这个问题吗?
我正在使用MySQL
提前致谢,Steve-O
答案 0 :(得分:4)
确切的细节将取决于DBMS和数据库的性质(例如,面向OLAP和面向OLTP),但一种常见的通用方法是创建一个表示辅助calendar
的表日期为dimension。然后你可以使用常规的JOIN
,而不必使用复杂的逻辑来生成缺少的日期。
this StackOverflow question的答案描述了如何在MySQL上应用这种方法。
顺便说一下,你可以通过numbers
个表格对数字使用类似的方法;我自己从来没有为数字做过这件事,但这似乎是一个流行的想法;见this dba.stackexchange.com question。
答案 1 :(得分:1)
如果您使用的是SQL Server 2005或更高版本,则可以使用CTE(如果不是,使用循环或其他SQL技术来填充范围内日期的表)。另请注意,CTE中的递归级别有限制。
declare @dateRange table
(
dateBegin datetime,
dateEnd datetime
)
insert into @dateRange (dateBegin, dateEnd)
values ('2012-01-01', '2012-01-07')
;with cte (d)
as (select dateBegin as d
from @dateRange tbl
where datediff(day, tbl.dateBegin, tbl.dateEnd) <= 100
union all
select dateadd(day, 1, cte.d) as d
from cte
inner join @dateRange tbl on cte.d < tbl.dateEnd)
然后使用CTE或包含范围中日期集的临时表获得完整结果:
select cte.d, sum(isnull(e.errorCounter, 0))
from cte
left outer join @errors e on e.errorDate = cte.d
group by cte.d
order by cte.d
答案 2 :(得分:0)
你真的应该在应用层处理这个问题(即迭代已知的日期范围并从结果集中提取非零值)或修复你的表,如果你必须有一个以数据库为中心的解决方案,那么总是要包含所需的日期。实时生成一组用于构建连续日期范围查询的日期的方法并不是很好。
您可以在DB脚本解决方案的一些示例中看到这一点:
Return temp table of continuous dates
但我认为你提出了错误的问题。修复数据库以包含您需要的内容,或修复生成报告的方式。数据库不是要进行插值和数据生成。