Маленький этюд (MSSQL)
От: 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒  
Дата: 14.08.23 16:38
Оценка:
Есть лог вида:

Date (nin-unique) Log
15-Jan-2023 18:45:53 Whatever
...


Нужно написать функцию, принимающую @DateFrom + @DateTo, определющие интервал выборки и возвращающую fill factor % заполненности лога поминутно, почасово, подневно. Пусть будет только поминутно. Процент вычисляется как % наличие записей на каждую из минут внутри интервала. то есть если в логе 1 запись как вверху, и интервал 15-Jan-2023 18:00:00 — 15-Jan-2023 19:00:00 (для педантов — "с" включаем, "по" — исключаем), то % будет 1/60 = 1.(6)%, если будет 500 записей датированных той же самой минутой, то результат не поменяется, если будет запись, например, 15-Jan-2023 18:46:53, то результат увеличится вдвое.

должно работать быстро, решение необходимо для MSSQL, оконные функции недоступны.

мой подход был конвертировать в то, по чему можно делать дистинкт, что работает, но, интересно, есть ли решение лучше.
Отредактировано 14.08.2023 19:05 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒ . Предыдущая версия .
Re: Маленький этюд (MSSQL)
От: Olaf Россия  
Дата: 15.08.23 05:37
Оценка:
Здравствуйте, 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒, Вы писали:

尿Ǥ푙>должно работать быстро, решение необходимо для MSSQL, оконные функции недоступны.


尿Ǥ푙>мой подход был конвертировать в то, по чему можно делать дистинкт, что работает, но, интересно, есть ли решение лучше.


Рассматриваем только поминутный вариант. Считаем, что даты @DateFrom и @DateTo, содержат время кратное часу, иначе эти параметры нужно пред обрабатывать.
Альтернатив реализации, честно говоря, я не вижу. Выбираем данные за период, отрезаем секунды и находим количество уникальных записей по дате.

drop table if exists #Log
create table #Log(TimeStamp datetime)

insert into #Log(TimeStamp) select '20230115 18:45:53'
insert into #Log(TimeStamp) select '20230115 18:40:53'
insert into #Log(TimeStamp) select '20230115 18:41:53'
insert into #Log(TimeStamp) select '20230115 18:42:53'
insert into #Log(TimeStamp) select '20230115 18:44:53'
insert into #Log(TimeStamp) select '20230115 18:43:53'
insert into #Log(TimeStamp) select '20230115 18:43:53'
insert into #Log(TimeStamp) select '20230115 18:43:53'
insert into #Log(TimeStamp) select '20230115 18:43:53'
insert into #Log(TimeStamp) select '20230115 18:43:53'
insert into #Log(TimeStamp) select '20230115 18:43:53'
insert into #Log(TimeStamp) select '20230115 18:43:53'

declare @DtFrom datetime = '20230115 18:00:00'
declare @DtTo datetime = '20230115 18:00:00'

select
    100 * count
    (
        distinct dateadd(minute, datediff(minute, cast(0 as datetime), l.TimeStamp), cast(0 as datetime))
    ) / 
    (
        nullif(datediff(minute, @DtFrom, @DtTo), 0) * 1.0
    ) as LogFillFactor
from
    #Log l
where
    l.TimeStamp between @DtFrom and @DtTo


P.S. Зачем ввели ограничение на оконные функции – не понял.
Re[2]: Маленький этюд (MSSQL)
От: 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒  
Дата: 15.08.23 08:02
Оценка:
UPDATE

есть идея зайти с другой стороны — создать Range для искомого интервала дат с bit флагом и апдейтить его по exists данных из лога, не нужно конвертировать и дистинктить ничего — нет времени пробовать, думаешь, есть шанс?

---

Я делал через select count(distinct(datediff(minute, ..., ...)) как и ты, на больших логах работает не быстро.

O>P.S. Зачем ввели ограничение на оконные функции – не понял.

compatibility level, у нас функиции типа stuff самописные.
Отредактировано 15.08.2023 8:09 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒ . Предыдущая версия .
Re[3]: Маленький этюд (MSSQL)
От: Olaf Россия  
Дата: 15.08.23 10:38
Оценка: 4 (1)
Здравствуйте, 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒, Вы писали:

尿Ǥ푙>UPDATE


尿Ǥ푙>есть идея зайти с другой стороны — создать Range для искомого интервала дат с bit флагом и апдейтить его по exists данных из лога, не нужно конвертировать и дистинктить ничего — нет времени пробовать, думаешь, есть шанс?


尿Ǥ푙>---


尿Ǥ푙>Я делал через select count(distinct(datediff(minute, ..., ...)) как и ты, на больших логах работает не быстро.


O>>P.S. Зачем ввели ограничение на оконные функции – не понял.

尿Ǥ푙>compatibility level, у нас функиции типа stuff самописные.

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

Пробовать вариант с созданием набора данных в интервале дат, можно, но полагаю выигрыш будет незначительный. В логе как я понял, хранится время с секундами, поэтому перед выбором данных эти секунды нужно обрезать. Соответственно поиск по индексу будет работать только в интервале заданных дат l.TimeStamp between @DtFrom and @DtTo, а не по конкретному значению l.TimeStamp = c.Dt1 Важным фактором так же будет являться распределение данных в этом интервале дат за час, т.е. общее количество записей и количество дубликатов.

Как вариант я набросал пример с созданием набора данных в интервале. Но нужен сервер 2012+ для использования рекурсии в CTE. У меня выигрыш по стоимости плана запроса составил 4%. Данные представляют собой 4 млн. записей с посекундным интервалом.

  Запрос
declare @DtFrom datetime = '20230814 18:00:00'
declare @DtTo datetime = '20230814 19:00:00'

;with Intervals as
(
    select @DtFrom as Dt1
    union all
    select
        dateadd(minute, 1, Dt1) 
    from
        Intervals c
    where
        dateadd(minute, 1, Dt1) < @DtTo
)
select
    100 * count
    (
        dateadd(minute, datediff(minute, 0, c.Dt1), 0)
    ) / 
    (
        nullif(datediff(minute, @DtFrom, @DtTo), 0) * 1.0
    ) as LogFillFactor
from
    Intervals c
where
    exists
    (
        select
            1
        from
            #Log l
        where
            l.TimeStamp between @DtFrom and @DtTo
            and dateadd(minute, datediff(minute, cast(0 as datetime), l.TimeStamp), cast(0 as datetime)) = c.Dt1
    )
option (maxrecursion 0)
Re[4]: Маленький этюд (MSSQL)
От: 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒  
Дата: 15.08.23 12:53
Оценка:
я не спец по базам если что, но вот вариант запроса:

-- пишу в notepad'e
fill_factor = 100 * (select count(distinct(datediff(minute, '1-jan-1970', log_time))) from log) / datediff(minute, @date_from, @date_to)

у меня 130 уровень поэтому APPROX_COUNT_DISTINCT недоступен, было бы интересно посмотреть, сколько он дает в замен на то что просит, прогонишь если не сложно?
Отредактировано 15.08.2023 12:55 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒ . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.