SQL Server: среднее количество предупреждений в день, не включая дни без предупреждений - PullRequest
0 голосов
/ 24 января 2019

У меня есть таблица, которая действует как журнал сообщений, с двумя таблицами ключей: TIMESTAMP и TEXT.Я работаю над запросом, который захватывает все оповещения (от TEXT) за последние 30 дней (на основе TIMESTAMP) и выдает среднесуточное значение для этих оповещений.

Вот запрос на данный момент:

--goback 30 days start at midnight
declare @olderdate as datetime
set @olderdate = DATEADD(Day, -30, DATEDIFF(Day, 0, GetDate()))

--today at 11:59pm
declare @today as datetime
set @today = dateadd(ms, -3, (dateadd(day, +1, convert(varchar, GETDATE(), 101))))
print @today

--Grab average alerts per day over 30 days
select 
    avg(x.Alerts * 1.0 / 30)
from
    (select count(*) as Alerts 
     from MESSAGE_LOG 
     where text like 'The process%' 
       and text like '%has alerted%'
       and TIMESTAMP between @olderdate and @today) X

Однако я хочу добавить что-то, что проверяет, были ли какие-либо оповещения за день и, если нет оповещений за этот день,не включает это в среднем.Например, если в месяц имеется 90 оповещений, но все они в один день, я бы не хотел, чтобы среднее число оповещений составляло 3 оповещения в день, поскольку это явно вводит в заблуждение.

Есть ли способ включить это в мой запрос?Я искал другие решения для этого, но не смог заставить их работать.

Ответы [ 3 ]

0 голосов
/ 24 января 2019

Вместо деления на 30, чтобы получить среднее значение, разделите на количество различных дней в ваших результатах.

select 
    avg(x.Alerts * 1.0 / x.dd)
from
    (select count(*) as Alerts, count(distinct CAST([TIMESTAMP] AS date)) AS dd
...
0 голосов
/ 24 января 2019

Вы пытаетесь вычислить двойное агрегирование: среднее значение ежедневных итогов.

Не используя CTE , вы также можете попробовать это, что обобщенно работает несколько месяцев.

--get a list of events per day
DECLARE @Event TABLE
(
    ID INT NOT NULL IDENTITY(1, 1)
    ,DateLocalTz DATE NOT NULL--make sure to handle time zones
    ,YearLocalTz AS DATEPART(YEAR, DateLocalTz) PERSISTED
    ,MonthLocalTz AS DATEPART(MONTH, DateLocalTz) PERSISTED
)
/*
INSERT INTO @Event(EntryDateLocalTz)
SELECT DISTINCT CONVERT(DATE, TIMESTAMP)--presumed to be in your local time zone because you did not specify
FROM dbo.MESSAGE_LOG
WHERE UPPER([TEXT]) LIKE 'THE PROCESS%' AND UPPER([TEXT]) LIKE '%HAS ALERTED%'--case insenitive
*/
INSERT INTO @Event(DateLocalTz)
VALUES ('2018-12-31'), ('2019-01-01'), ('2019-01-01'), ('2019-01-01'), ('2019-01-12'), ('2019-01-13')
--get average number of alerts per alerting day each month
--  (this will not return months with no alerts,
--  use a LEFT OUTER JOIN against a month list table if you need to include uneventful months)
SELECT
    YearLocalTz
    ,MonthLocalTz
    ,AvgAlertsOfAlertingDays = AVG(CONVERT(REAL, NumDailyAlerts))
FROM
    (
        SELECT
            YearLocalTz
            ,MonthLocalTz
            ,DateLocalTz
            ,NumDailyAlerts = COUNT(*)
        FROM @Event
        GROUP BY YearLocalTz, MonthLocalTz, DateLocalTz
    ) AS X
GROUP BY YearLocalTz, MonthLocalTz
ORDER BY YearLocalTz ASC, MonthLocalTz ASC

Что нужно отметить в моем коде:

  1. Я использую PERSISTED столбцы, чтобы получить части даты месяца и года (потому что я ленив при заполнении таблиц)
  2. Используйте явный CONVERT, чтобы избежать целочисленной математики, которая округляет десятичные дроби. Умножение на 1,0 - менее читабельный хак.
  3. Используйте CONVERT(DATE, ...) для округления до полуночи вместо преобразования туда и обратно между строками
  4. Выполните поиск строк без учета регистра, указав все в верхнем или нижнем регистре (по вашему выбору)
  5. Не вычитайте 3 миллисекунды, чтобы получить самый последний момент до полуночи. Измените свою семантику, чтобы интерпретировать конец временного диапазона как исключительный, вместо того, чтобы иметь дело с точностью ваших типов данных. Единственная разница заключается в использовании явных компараторов (то есть используйте < вместо <=). Кроме того, разрешение DATETIME составляет 1/300 th секунды , а не 3 миллисекунды.
  6. Избегайте использования встроенных ключевых слов в качестве имен столбцов (т. Е. «ТЕКСТ»). Если это так, оберните их в квадратные скобки, чтобы избежать двусмысленности.
0 голосов
/ 24 января 2019

Это не написано для вашего запроса, так как у меня нет DDL или примеров данных, поэтому я приведу очень простой пример вместо того, как вы это сделаете.

USE Sandbox;
GO

CREATE TABLE dbo.AlertMessage (ID int IDENTITY(1,1),
                               AlertDate date);

INSERT INTO dbo.AlertMessage (AlertDate)
VALUES('20190101'),('20190101'),('20190105'),('20190110'),('20190115'),('20190115'),('20190115');
GO

--Use a CTE to count per day:
WITH Tots AS (
    SELECT AlertDate,
           COUNT(ID) AS Alerts
    FROM dbo.AlertMessage
    GROUP BY AlertDate)
--Now the average
SELECT AVG(Alerts*1.0) AS DayAverage
FROM Tots;
GO

--Clean up
DROP TABLE dbo.AlertMessage;
...