2012-02-10 2 views
1

У меня есть таблица, в которой хранится количество ошибок в зависимости от того, какой идентификатор тревоги он есть. Таблица выглядит примерно так:Получить дату, даже если она не существует в таблице из инструкции SQL SELECT

|----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 не существует, возвращает идентификатор в любом случае со значением COUNTER 0. Таким образом:

 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

Какие СУБД? (Oracle, PostgreSQL, SQL Server, MySQL, ...?) – ruakh

+0

Я использую MySQL – ClydeFrog

ответ

4

Точные данные будут зависеть от СУБД и от характера базы данных (например, ориентированной на OLAP и OLTP-ориентированной), но один общий общий подход заключается в создании вспомогательной таблицы calendar, которая представляет даты как dimension. Затем вы можете использовать обычный JOIN s, вместо того, чтобы использовать сложную логику для генерации отсутствующих дат.

Ответы на this StackOverflow question описывают, как применять этот подход в MySQL.

Вы можете использовать аналогичный подход для чисел, кстати, имея таблицы numbers; Я никогда не делал этого для чисел, но, похоже, это популярная идея; см. this dba.stackexchange.com question.

+0

Я использую MySQL. Прошу прощения за отсутствие информации, я также отредактировал свой вопрос – ClydeFrog

+0

@ Steve-O: Да, я отредактировал свой ответ несколько раз, увидев это обновление. :-) – ruakh

0

Вы действительно должны справиться с этим на уровне приложений (т.е. перебрать известный диапазон дат и тянуть ненулевые Vals от resultset) или исправить вашу таблицу, чтобы всегда включать нужные даты, если вы ДОЛЖНЫ иметь решение, ориентированное на базу данных. Существует не очень хороший способ генерировать «на лету» набор дат для создания непрерывного запроса диапазона дат.

Вы можете увидеть это некоторые примеры сценариев решений DB:

Return temp table of continuous dates

Но я думаю, что вы создает неправильный вопрос. Исправьте базу данных, чтобы включить то, что вам нужно, или исправьте, как вы создаете отчет. Базы данных не предназначены для интерполяции и данных поколения.

+0

Существует действительно очень хороший способ создать «на лету» набор дат: предварительно заполненную таблицу календаря. –

+0

Конечно, но это не значит «на лету». :-) Это подход, рекомендованный на странице, с которой я связан, и она будет работать нормально, но все-таки ... код, по моему мнению, обоняет весь подход к проблеме. –

+0

Это не поколение «на лету», оно производит «на лету». И «запах кода»? В самом деле? Зачем? Если вам нужны эти запросы диапазона часто, нет причин не проводить вычисления раньше. Наличие таблицы календаря в течение 2-3 веков будет использовать около 1-20 МБ (в зависимости от деталей), которая является арахисами для базы данных. –

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) 

Затем получить полные результаты либо с помощью КТР или временную таблицу, содержащую набор дат в диапазоне:

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 
Смежные вопросы