Получить дату, даже если она не существует в таблице из оператора SQL SELECT - PullRequest
1 голос
/ 10 февраля 2012

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

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

У меня есть запрос, который возвращает идентификаторы, даже если они не существуют в таблице, и, если идентификатор не существует, в любом случае верните идентификатор со значением 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

Заранее спасибо, Стив-О

Ответы [ 3 ]

4 голосов
/ 10 февраля 2012

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

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

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

1 голос
/ 10 февраля 2012

Если вы используете 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
0 голосов
/ 10 февраля 2012

Вы действительно должны обработать это на прикладном уровне (то есть выполнить итерацию по известному диапазону дат и извлечь ненулевые значения из набора результатов) или исправить свою таблицу, чтобы всегда включать даты, необходимые, если у вас ОБЯЗАТЕЛЬНО есть решение, ориентированное на базу данных , Не существует действительно хорошего способа генерировать на лету набор дат для построения непрерывного запроса в диапазоне дат.

Вы можете увидеть это для некоторых примеров решений сценариев БД:

Возвращение временной таблицы непрерывных дат

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

...