KQL — разбивка временного интервала нахождения элемента в определенном состоянии по дням

Я пытаюсь рассчитать, как долго элемент был активен в определенном состоянии с помощью KQL. В настоящее время я использую оконные функции вместе с секционированием. Это хорошо работает, когда мне нужно вычислить общее «время в состоянии» за определенный период, но проблема возникает при попытке разбить его на день (или любую произвольную степень детализации).

Вход:

let inputData=datatable(id:string, status: string, timestamp: datetime) [ 
    "id1","P",datetime(2024-03-12T05:30:15),
    "id1","F",datetime(2024-03-14T10:10:00),
    "id2","P",datetime(2024-03-12T05:30:15)
];
let startDate=datetime(2024-03-12T00:00:00);
let endDate=datetime(2024-03-15T00:00:00);

Применение следующего запроса дает общее время, которое каждый идентификатор провел в каждом состоянии:

inputData
| partition hint.strategy=native by id
    (
        order by timestamp asc
        | extend tsDiff = min_of(endDate, next(timestamp)) - timestamp
        | extend pTime = iif (status == "P", tsDiff, timespan(0))
        | extend fTime = iif (status == "F", tsDiff, timespan(0))
    )
    | summarize totalPTime=sum(pTime), totalFTime=sum(fTime) by id

Results:
id  totalPTime  totalFTime
id1 2.04:39:45  13:50:00
id2 2.18:29:45  00:00:00

Теперь я в тупике, как разбить это на повседневную жизнь. Попытка использовать, например, make-series дает:

inputData
| partition hint.strategy=native by id
    (
        order by timestamp asc
        | extend tsDiff = min_of(endDate, next(timestamp)) - timestamp
        | extend pTime = iif (status == "P", tsDiff, timespan(0))
        | extend fTime = iif (status == "F", tsDiff, timespan(0))
    )
    | make-series 
        totalP=sum(pTime),
        totalF=sum(fTime) on timestamp from startDate to endDate step 1d by id

Results:
id  totalP  totalF  timestamp
id1 ["2.04:39:45","00:00:00","00:00:00"]    ["00:00:00","00:00:00","13:50:00"]  ["2024-03-12","2024-03-13","2024-03-14"]
id2 ["2.18:29:45","00:00:00","00:00:00"]    ["00:00:00","00:00:00","00:00:00"]  ["2024-03-12","2024-03-13","2024-03-14"]

Мне нужны результаты максимум за 1 день и переполнение на следующий день, если это имеет смысл. Например:

id  totalP  totalF  timestamp
id1 ["1.00:00:00","1.00:00:00","04:39:45"]  ["00:00:00","00:00:00","13:50:00"]  ["2024-03-12","2024-03-13","2024-03-14"]
id2 ["1.00:00:00","1.00:00:00","18:29:45"]  ["00:00:00","00:00:00","00:00:00"]  ["2024-03-12","2024-03-13","2024-03-14"]

Нахожусь ли я на правильном пути или мне следует использовать для этой цели какую-то другую функциональность? Любая помощь очень ценится!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
310
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете использовать оператор mv-apply для добавления записей на каждый день:

inputData
| partition hint.strategy=native by id
    (
        order by timestamp asc
        | extend dur = next(timestamp, 1, endDate) - timestamp
        | mv-apply td = range(0d, dur, 1d) to typeof(timespan) on (
            extend tsDiff = iff(dur - td >= 1d, 1d, dur - td)
            | extend timestamp = timestamp + td
        )
        | extend pTime = iif (status == "P", tsDiff, timespan(0))
        | extend fTime = iif (status == "F", tsDiff, timespan(0))
    )
    | make-series 
        totalP=sum(pTime),
        totalF=sum(fTime) on timestamp from startDate to endDate step 1d by id

Спасибо за ответ. Это решение выглядит очень элегантно, но оно сталкивается с проблемами в зависимости от входных данных. Возможно, данные моего примера не были оптимальными в этом вопросе, но учтите следующее: "id1","P",datetime(2024-03-12T00:00:00), "id1","F",datetime(2024-03-12T02:00:00), "id1","P",datetime(2024-03-12T03:00:00), Доходность: totalP: ["1.02:00:00","1.00:00:00","21:00:00"]

Mimir 19.03.2024 09:46
Ответ принят как подходящий

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

Я уверен, что придет кто-то другой и предложит вам гораздо более красноречивое и эффективное в вычислительном отношении решение, но я верю, что вы находитесь на правильном пути в том, что делаете. Чего, похоже, не хватает, так это того, что, как и большинство языков запросов, KQL не умеет показывать то, чего нет. Поэтому я лично предпочитаю всегда начинать с того, что мы знаем. Для вашего вопроса я взял уже имеющиеся у вас временные диапазоны.

Здесь имеется большое количество разделов (как вы использовали), диапазона , сканирования и динамических переменных.

У этого решения есть некоторые предостережения, а именно: в его текущем формате оно имеет точность только до минуты, нам может не понадобиться вычислять окончательный статус, и я делаю вывод, что статус всегда может быть только P или F.

Я знаю, что я не рассчитываю время так же, как ваш запрос, но, надеюсь, вы сможете настроить его по мере необходимости.

let inputData=datatable(id:string, status:string, timestamp: datetime) [ 
    "id1","P",datetime(2024-03-12T05:30:15),
    "id1","F",datetime(2024-03-14T10:10:00),
    "id2","P",datetime(2024-03-12T05:30:15)
];
let units = 1m; //eg 1 minute calculating
let startDate = floor(datetime(2024-03-12T00:00:00), units);
let endDate = floor(datetime(2024-03-15T03:00:00), units);
range timestamp from startDate to endDate step units
| extend JoinHere = 1
| join kind=leftouter (inputData | summarize by id, JoinHere = 1) on JoinHere //Create an entry for every possible timestamp and id
| join kind=leftouter (inputData
| summarize arg_min(timestamp, status) by id //Get the first change
| extend status = iif (status == 'P', 'F', 'P') //Assume prior to this it was on the other status
| extend timestamp = startDate //Start it at the beginning of our window
) on timestamp, id
| join kind=leftouter (inputData
| summarize arg_max(timestamp, status) by id //Get the last change
| extend timestamp = endDate //End it at the end of the window
) on timestamp, id
| order by timestamp asc
| join kind=leftouter (inputData | extend timestamp = floor(timestamp, units)) on timestamp, id //Mix in our known changes
| extend status = coalesce(status, status1, status2) //From all the joins have a single value for status
| project timestamp, id, status
| partition hint.strategy=native by id
    (
    sort by timestamp asc
    | scan declare (status:string) with
       (
       step Step1: true => status = iif (isempty(status), Step1.status, status);
       )
    )
| partition hint.strategy=native by id
   (
   summarize TimeUnits = array_length(make_set(timestamp)) by floor(timestamp, 1d), status, id
   | extend pTime = iif (status == "P", TimeUnits, 0) //Unit in this case is 1 minute as above
   | extend fTime = iif (status == "F", TimeUnits, 0)
   )
| make-series 
  totalP=sum(pTime),
  totalF=sum(fTime) on timestamp from startDate to endDate step 1d by id
идентификатор всегоP всегоF временная метка идентификатор1 [1110,1440,610,0] [330,0,830,181] [""2024-03-12T00:00:00.0000000Z","2024-03-13T00:00:00.0000000Z","2024-03-14T00:00:00.0000000Z","2024-03-15T00:00: 00.0000000Z""] идентификатор2 [1110,1440,1440,181] [330,0,0,0] [""2024-03-12T00:00:00.0000000Z","2024-03-13T00:00:00.0000000Z","2024-03-14T00:00:00.0000000Z","2024-03-15T00:00: 00.0000000Z""]

Другие вопросы по теме