即使SQL SELECT语句中的表中不存在日期,也要获取日期

时间:2012-02-10 13:06:27

标签: mysql sql date select exists

我有一个表,根据警报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

3 个答案:

答案 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

但我认为你提出了错误的问题。修复数据库以包含您需要的内容,或修复生成报告的方式。数据库不是要进行插值和数据生成