# Создание запросов в KUMA (CookBook)

Этакий "CookBook" по различным интересным запросам в KUMA (Поиск событий)

# Запросы в KUMA (примеры)

<p class="callout info">Описание функций ClickHouse для работы с запросами: [https://clickhouse.com/docs/ru/sql-reference/functions/](https://clickhouse.com/docs/ru/sql-reference/functions/) </p>

<p class="callout info">Запрос из интерфейса пробрасывается в БД с добавлением границ временного промежутка и выбранных тенантов, пример: `... <span class="ui-provider a b c d e f g h i j k l m n o p q r s t u v w x y z ab ac ae af ag ah ai aj ak" dir="ltr">AND (Timestamp >= 1715689595208 AND Timestamp <= 1715689895208) AND (TenantID IN ('a1fbde7a-76d3-4bbc-a769-82126b41b56f')) ORDER BY ...</span>`</p>

## <iframe class="align-center" height="315" src="https://www.youtube.com/embed/FnlT2hh_ACk?si=ktOOgrB_Q7rBL5wm" style="border:0;" title="YouTube video player" width="560"></iframe>



## Время в событиях

Если в событии присутствует информация о таймзоне (deviceTimeZone) или корректно парсится timestamp события в нормализаторе, где получаем ±hh:mm и маппится это значение в поле DeviceTimeZone - событие поступает в KUMA со значением этой таймзоны

Если в событии нет deviceTimeZone или время из события не содержит таймзону, которую можно распарсить, то для такого события устанавливается таймзона сервера на котором установлен коллектор.

Таймзону еще можно поправить следующим образом:

- создать правило обогащения типа таймзона и скорректировать временную зону;
- в поисковый запрос добавить константу с таймзоной (пример, SELECT '+0300' as deviceTimeZone), можно так же указать таймзону формата +03:00 или +03.

## Базовые запросы (поиск событий)


---

### Типы событий в KUMA

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/D8jimage.png)

---

### Подсчет событий по полю

```sql
SELECT count(ID) as count_num, DeviceVendor
FROM `events`
GROUP BY DeviceVendor
ORDER BY count_num DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/RcAimage.png)

---

### Выполнение математических операций и сравнений

```sql
SELECT DeviceProduct, SourceUserName, round(sum(BytesIn)/1024, 2) as KiloBytes 
FROM `events` 
WHERE BytesIn > '0' OR BytesOut > '0'
GROUP by SourceUserName, DeviceProduct
ORDER BY KiloBytes DESC LIMIT 250
```

Иногда бОльшую производительность дает условие со скобками `WHERE (BytesIn > '0' OR BytesOut > '0')`

---

### По подстроке (регистрозависимый) с условием И

```sql
SELECT *
FROM `events`
WHERE DeviceHostName like '%serv%' AND DeviceProduct = 'Windows'
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/wH8image.png)

---

### По подстроке (регистроНЕзависимый)

```sql
SELECT *
FROM `events`
WHERE DeviceEventCategory ilike '%auditing%' AND DeviceProduct = 'Windows'
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/W5Eimage.png)

---

### По исходному / сырому событию

```sql
SELECT *
FROM `events`
WHERE Raw ilike '%technique%'
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-10/scaled-1680-/T2Ximage.png)

---

### По нескольким значениям, вместо OR можно использовать IN

```sql
SELECT *
FROM `events`
WHERE DeviceEventClassID IN ('BROKER_USERLOGGEDOUT', 'BROKER_USERLOGGEDIN')
AND DeviceProduct = 'Horizon'
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/jKLimage.png)

---

### По подсети

```sql
SELECT *
FROM `events`
WHERE inSubnet(DeviceAddress, '10.68.85.70/32')
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/uGjimage.png)

### Внешние IPv4 адреса

```sql
SELECT count(ID) as count, DestinationAddress 
FROM `events` 
WHERE NOT empty(DestinationAddress) AND NOT (inSubnet(DestinationAddress, '10.0.0.0/8') OR inSubnet(DestinationAddress, '172.16.0.0/12') OR inSubnet(DestinationAddress, '192.168.0.0/16') OR inSubnet(DestinationAddress, '127.0.0.0/8')) 
AND NOT isIPv6String(DestinationAddress) 
GROUP BY DestinationAddress 
ORDER BY count DESC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-12/scaled-1680-/gUdimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2024-12/gUdimage.png)


### По отсутствующему полю

```sql
SELECT *
FROM `events`
WHERE empty(DeviceEventClassID) AND Raw ilike '%backdoor_user%'
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-08/scaled-1680-/Vveimage.png)

---

### По регулярному выражению

```sql
SELECT * 
FROM `events` 
WHERE match(DestinationUserName, '^\w+\-\d\$$')  
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-10/scaled-1680-/nDFimage.png)

---

### По регулярному выражению с выбором группы

```sql
SELECT count(ID) AS `metric`, regexpExtract(FlexString1,'^((https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=&#]{1}[\da-z\.-]+)*\S+)$', 1) AS `value`
FROM `events` 
WHERE DeviceProduct = 'DFI' AND `value` != ''
GROUP BY FlexString1 
ORDER BY `metric` DESC 
LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/scaled-1680-/kxbimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/kxbimage.png)

---

## Проверка работы обогащения

---

### Обогащение событий Активами  


```sql
SELECT *
FROM `events`
WHERE (DeviceAssetID != '' OR SourceAssetID != '' OR DestinationAssetID != '')
ORDER BY Timestamp DESC LIMIT 250
```

---

### Обогащение событий с LDAP   


```sql
SELECT *
FROM `events`
WHERE (SourceAccountID != '' OR DestinationAccountID != '')
ORDER BY Timestamp DESC LIMIT 250
```

---

### Обогащение событий данными из TI  


```sql
SELECT *
FROM `events`
WHERE NOT TI=''
ORDER BY Timestamp DESC LIMIT 250
```

---

### Сортировка по текстовому значению (Severity)  


Сортировка от Critical до Low

```sql
SELECT ExternalID AS `ID Обнаружения`, Severity AS `Критичность`, DeviceEventCategory AS `Категория`, FlexString1 AS `Объект`, JSONExtractString(Extra, 'name') AS `Описание`, DeviceCustomString5 AS `Рекомендация`
FROM `events` WHERE DeviceProduct = 'DFI'
ORDER BY indexOf(['Critical', 'High', 'Medium', 'Low'], Severity)
LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/scaled-1680-/image.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/image.png)

---

## Работа с Extra полем

---

### По полю Extra содержащие ключ  


```sql
SELECT *
FROM `events`
WHERE visitParamHas(Extra, 'memUsage')
ORDER BY Timestamp DESC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/y7Pimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/y7Pimage.png)

---

### По полю Extra содержащие ключ с определенным значением  


```sql
SELECT *
FROM `events`
WHERE visitParamExtractString(Extra, 'memUsage') = '61367'
ORDER BY Timestamp DESC LIMIT 250
```

```sql
SELECT *
FROM `events`
WHERE JSONExtractString(Extra, 'memUsage') = '61367'
ORDER BY Timestamp DESC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/lhSimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/lhSimage.png)

---

### По полю Extra НЕ содержащие ключ с определенным значением

```sql
SELECT *
FROM `events`
WHERE visitParamHas(Extra, 'memUsage') 
AND NOT visitParamExtractString(Extra, 'memUsage') = '61367' 
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/cWjimage.png)

### Подсчет количества вхождений в массив

```sql
SELECT sum(length(extractAll(Extra, '"tags\\.[0-9]+":'))) AS `metric`
FROM `events` 
WHERE DeviceProduct = 'DFI' AND DeviceEventCategory = 'vulnerability' 
LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/scaled-1680-/LtHimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/LtHimage.png)

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/scaled-1680-/YnNimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/YnNimage.png)

---

## Работа со временем 

Timestamp по умолчанию в формате epoch time числа с милисекундами (UnixTimestamp)

<p class="callout info">Список тайм зон: [https://en.wikipedia.org/wiki/List\_of\_tz\_database\_time\_zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) </p>

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/qvUimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/qvUimage.png)

---

### Задание таймзоны

```sql
SELECT Timestamp, fromUnixTimestamp64Milli(Timestamp, 'Europe/Moscow') as NormTime,
DeviceProduct, Name, Message
FROM `events` 
WHERE DeviceProduct = 'EDR'
ORDER BY Timestamp DESC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/rHRimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/rHRimage.png)

---

### Указание своего формата времени

```sql
SELECT Timestamp, formatDateTime(fromUnixTimestamp64Milli(Timestamp), '%d-%m-%Y %H:%i:%S') as NormTime,
DeviceProduct, Name, Message
FROM `events`
WHERE DeviceProduct = 'EDR'
ORDER BY Timestamp DESC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/u8bimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/u8bimage.png)

---

### Свой формат времени с таймзоной

```sql
SELECT Timestamp,
formatDateTime(fromUnixTimestamp64Milli(Timestamp), '%d-%m-%Y %H:%i:%S', 'Europe/Moscow') as NormTime,
DeviceProduct, Name, Message
FROM `events`
WHERE DeviceProduct = 'EDR'
ORDER BY Timestamp DESC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/YNHimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/YNHimage.png)

---

### События в "рабочее время" с 9 до 18

```sql
SELECT Timestamp,
formatDateTime(fromUnixTimestamp64Milli(Timestamp), '%d-%m-%Y %H:%i:%S', 'Europe/Moscow') as NormTime,
DeviceProduct, Name, Message 
FROM `events` 
WHERE DeviceProduct = 'EDR' AND toHour(fromUnixTimestamp64Milli(Timestamp, 'Europe/Moscow')) >= 9
AND toHour(fromUnixTimestamp64Milli(Timestamp, 'Europe/Moscow')) < 18 
ORDER BY Timestamp ASC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/w7Jimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/w7Jimage.png)

---

### Подсчет события по фильтру за период вчерашнего дня с 00 до 04 часов с группировкой по SourceAddress

```sql
SELECT count(ID) as Count_Num, SourceAddress, groupArray(fromUnixTimestamp64Milli(Timestamp)) as time_stm
FROM `events` 
WHERE DeviceEventClassID = '4624' AND DeviceProduct = 'Windows'
AND Timestamp >= toUnixTimestamp(toStartOfDay((now() - INTERVAL 1 DAY)))*1000
AND Timestamp <= toUnixTimestamp(toStartOfDay((now() - INTERVAL 1 DAY)) + INTERVAL 4 HOUR)*1000
GROUP BY SourceAddress LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/Ye0image.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/Ye0image.png)

---

### Подсчет уникальных адресов посетителей kb.kuma-community.ru

```sql
SELECT count(DISTINCT SourceAddress) as uniqSrcIP 
FROM `events` 
WHERE Code = '200' AND DeviceCustomString2 != '-' AND DestinationServiceName = 'apache_access'
AND DeviceCustomString3 = 'GET' AND DestinationHostName = 'kb' 
AND not inSubnet(SourceAddress, '91.103.66.0/24')
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-10/scaled-1680-/psGimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-10/psGimage.png)

Можно использовать аналогичную более быструю функцию:

```sql
SELECT uniq(SourceAddress) as uniqSrcIP
FROM `events`
WHERE Code = '200' AND DeviceCustomString2 != '-' AND DestinationServiceName = 'apache_access' 
AND DeviceCustomString3 = 'GET' AND DestinationHostName = 'kb'
AND not inSubnet(SourceAddress, '91.103.66.0/24') 
```

---

### Подсчет уникальных адресов посетителей kb.kuma-community.ru по дням

```sql
SELECT count(DISTINCT SourceAddress) as `metric`,
formatDateTime(fromUnixTimestamp64Milli(Timestamp), '%d-%m-%Y', 'Europe/Moscow') as value
FROM `events`
WHERE Code = '200' AND DeviceCustomString2 != '-' AND DestinationServiceName = 'apache_access'
AND DeviceCustomString3 = 'GET' AND DestinationHostName = 'kb'
AND not inSubnet(SourceAddress, '91.103.66.0/24')
GROUP BY value
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-11/scaled-1680-/np2image.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-11/np2image.png)

---

### Подсчет среднего количесва переданных байт посетителей kb.kuma-community.ru по источнику IP c 00:00 по 08:00

```sql
SELECT avg(toInt32(BytesOut)), SourceAddress
FROM `events`
WHERE BytesOut!= 0 AND Timestamp >= toUnixTimestamp(toStartOfDay((now() - INTERVAL 1 DAY)))*1000
AND Timestamp <= toUnixTimestamp(toStartOfDay((now() - INTERVAL 1 DAY)) + INTERVAL 8 HOUR)*1000
GROUP BY SourceAddress DESC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-11/scaled-1680-/537image.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-11/537image.png)

### Отображение количества переданных и принятых байт по внутренним адресам более 1 Гб

```sql
SELECT StartTime , EndTime , SourceAddress AS `SOURSE ADDRESS`, DestinationPort AS `TO PORT`, ApplicationProtocol AS `APPLICATION`, DeviceCustomString1 AS `RULE`, formatReadableSize(BytesIn) AS `SENT`, formatReadableSize(BytesOut) AS `RECEIVED`, formatReadableSize(FlexNumber1) AS `TOTAL` 
FROM `events` 
WHERE FlexNumber1 > 1000000000 
GROUP BY StartTime, EndTime, SourceAddress, DestinationPort, ApplicationProtocol, DeviceCustomString1, BytesIn, BytesOut, FlexNumber1 
ORDER BY `EndTime` DESC LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-06/scaled-1680-/pGZimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2024-06/pGZimage.png)

---

## Экстра запросы

---

### Склейка по времени подключений пользователей с одного адреса и на один VPN сервер

```sql
SELECT SourceUserName, SourceAddress, SourceHostName, groupArray(FlexString1) as time
FROM `events`
WHERE SourceProcessName = 'Create session'
GROUP BY SourceUserName, SourceAddress, SourceHostName LIMIT 10
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/rqIimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/rqIimage.png)

---

### Склейка различных адресов подключений от одного пользователя на VPN сервере

<p class="callout info">Оператор HAVING доступен с версии KUMA 3.0</p>

```sql
SELECT uniq(SourceAddress) as "Количество уникальных адресов", DestinationUserName  as "Имя пользователя", groupUniqArray(SourceAddress) as  "Список адресов"
FROM `events`
WHERE DeviceProduct = 'Ngate' AND Name = 'Create session'
GROUP BY "Имя пользователя"
HAVING "Количество уникальных адресов" > 1
ORDER BY "Количество уникальных адресов" DESC
LIMIT 10
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-04/scaled-1680-/fBuimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2024-04/fBuimage.png)

---

### Склейка наименования ПО и его версии

Пример строки Extra `desc: ## port 445 tcp / SMB version 1 is used`

Также с помощью replaceRegexpOne удаляем в конце слеш если нет заполненного транспорта с портом.

```sql
SELECT count(ID) as `metric`, replaceRegexpOne(concat(regexpExtract(JSONExtractString(Extra, 'desc'), '.+port\s(\d+)', 1), '/', regexpExtract(JSONExtractString(Extra, 'desc'), '.+port\s(\d+)\s+(tcp|udp)', 2)),'(.+)\/$','\\1') as `value`
FROM `events` WHERE DeviceProduct = 'DFI' AND value NOT IN('', '/')
GROUP BY value ORDER BY `metric` DESC
LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/scaled-1680-/orkimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-09/orkimage.png)

---

### Обрезка доменов до второго уровня и условие с несколькими запросами

```sql
SELECT count(ID) as cnt, cutToFirstSignificantSubdomain(DestinationHostName) as dstH
FROM `events`
WHERE DeviceCustomString1 = 'Q' AND DestinationProcessName = 'DNS'
AND DeviceCustomString5 IN ('A', 'AAAA', 'HTTPS')
GROUP BY dstH
ORDER BY cnt DESC LIMIT 50
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/FGfimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/FGfimage.png)

---

### Обрезка доменов до второго уровня и исключение IP адресов с помощью регулярного выражения

```sql
SELECT count(ID) as `metric`, cutToFirstSignificantSubdomain(RequestUrl) as `value`, 
match(`value`, '^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$') AS is_valid_domain  
FROM `events` 
WHERE DeviceProduct = 'UTM' AND FlexString2 = 'Instant Messaging' AND Type != 3 AND is_valid_domain = 1 
GROUP BY `value` ORDER BY  `metric` DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-11/scaled-1680-/K3nimage.png)

---

### Группировка событий по категории из TI (Regex)

```sql
SELECT count(ID) as cnt, extract(TI, '.+category\"\:\"([^"]+).+') as category
FROM `events`
WHERE TI !=''
GROUP BY category
ORDER BY cnt DESC
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-10/scaled-1680-/r5Bimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-10/r5Bimage.png)

---

### Все базовые события конкретного корреляционного события по его ID

```sql
SELECT *
FROM `events`
WHERE ID IN (
  SELECT arrayJoin(splitByChar(',', replaceRegexpAll(JSON_QUERY(BaseEvents, '$[*].ID'), '\\[|\\]|"| ', ''))) as TestID
  FROM `events`
  WHERE (Type = 3) AND (ID = '25e4eae3-ad6d-4114-b99e-019de3574d16')
)
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/scaled-1680-/9Bbimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2023-08/9Bbimage.png)

---

### Дерево процесса

```sql
SELECT
    b.DeviceHostName AS HostName, 
    b.SourceAccountID AS AccountID, 
    b.SourceUserName AS UserName, 
    a.DeviceCustomString3 AS GrandParentProcessID, 
    a.SourceProcessName AS GrandParentProcessName, 
    a.DeviceCustomString5 AS ParentProcessID, 
    a.DestinationProcessName AS ParentProcessName, 
    b.DeviceCustomString5 AS ProcessID, 
    b.DestinationProcessName AS ProcessName,
    concat(a.SourceProcessName, ' -> ', a.DestinationProcessName, ' -> ', b.DestinationProcessName) AS ProcessTree
FROM `events` AS a
INNER JOIN 
    (
        SELECT *
        FROM `events` 
        WHERE DeviceEventClassID='4688'
    ) AS b
ON a.DeviceCustomString5 = b.DeviceCustomString3
WHERE DeviceEventClassID='4688'
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-12/scaled-1680-/Zkhimage.png)

### Среднее время сессии Windows пользователей за 24 часа

Рассматривается запрос на основе событий Windows по пользователям (DestinationUserName) событиям входа (EventID 4624) и выхода (EventID 4634) с расчетом среднего времени сесии пользователя за последние 24 часа

```sql
SELECT
    login_events.DestinationUserName AS destination_user_name,
    round(AVG(logout_events.logout_time - login_events.login_time)/1000) AS avg_time_diff_s,
    COUNT(DISTINCT login_events.login_time) AS total_logins,
    COUNT(DISTINCT logout_events.logout_time) AS total_logouts,
    concat(
        toString(floor(avg_time_diff_s / 86400)), ' days, ',
        toString(floor((avg_time_diff_s % 86400) / 3600)), ' hours, ',
        toString(floor((avg_time_diff_s % 3600) / 60)), ' minutes, ',
        toString(avg_time_diff_s % 60), ' seconds'
    ) AS human_readable_diff
FROM 
    (SELECT
        DestinationUserName,
        toUnixTimestamp(EndTime) AS login_time,
        FlexString1 AS logon_id
    FROM `events`
    WHERE DeviceEventClassID = '4624'
    AND EndTime >= now('Europe/Moscow') - INTERVAL 24 HOUR
    AND DestinationUserName NOT LIKE '%$%') AS login_events
INNER JOIN 
    (SELECT
        DestinationUserName,
        toUnixTimestamp(EndTime) AS logout_time,
        FlexString1 AS logon_id
    FROM `events`
    WHERE DeviceEventClassID = '4634'
    AND EndTime >= now('Europe/Moscow') - INTERVAL 24 HOUR
    AND DestinationUserName NOT LIKE '%$%') AS logout_events

    ON login_events.DestinationUserName = logout_events.DestinationUserName 
    AND logout_events.logon_id = login_events.logon_id

WHERE logout_events.logout_time >= login_events.login_time 
GROUP BY login_events.DestinationUserName
ORDER BY avg_time_diff_s DESC 
LIMIT 100
```

Несмотря на ошибку запрос выполняется корректно:

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-01/scaled-1680-/Qgyimage.png)

### Частота и отношение неуспешных входов к успешным в Windows

```sql
SELECT 
count(CASE WHEN DeviceEventClassID = '4625' THEN 1 END) as `Failed logins`, 
count(CASE WHEN DeviceEventClassID = '4624' THEN 1 END) as `Success logins`, 
`Failed logins`/`Success logins` as `Ratio`
FROM 'events'
HAVING `Ratio` > 0.03
```

Или

```sql
SELECT countIf(DeviceEventClassID = '4625') as F, countIf(DeviceEventClassID = '4624') as S
FROM 'events'
LIMIT 250
```

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-01/scaled-1680-/Lstimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-01/Lstimage.png)

---

## Работа с кастомными полями (SA. NA.) 

### Вхождение элемента по полю

<p class="callout info">Поиск проискодит по точному совпадению с элементом</p>

```sql
SELECT * 
FROM `events` 
WHERE has(SA.user_groups, 'system:serviceaccounts') 
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-10/scaled-1680-/RJeimage.png)

---

### Вхождение по подстроке элемента по полю (где более 2 значений в массиве)

<p class="callout info">Счет в массиве начинается с нуля</p>

```sql
SELECT * 
FROM `events` 
WHERE length(arrayFilter(x -> x LIKE '%serviceaccounts%', SA.user_groups)) > 1 
ORDER BY Timestamp DESC LIMIT 250
```

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-10/scaled-1680-/iDFimage.png)

# Создание оптимизированных запросов

<p class="callout success">Другие параметры клика для оптимизации запросов: [https://clickhouse.com/docs/operations/settings/query-complexity](https://clickhouse.com/docs/operations/settings/query-complexity) </p>

Длительность выполнения запроса можно посмотреть при нажатии F12 (режим разработчика) в браузере Chrome после нажатия кнопки поиска:

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-09/scaled-1680-/sOHimage.png)

При создании запросов всегда явно выбирайте необходимый тенант с нужными событиями:

![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2024-09/scaled-1680-/HFoimage.png)

Если известны конкретные поля для поиска, то можно использовать более глубокий поиск по событиям. Старайтесть **НЕ** использовать поиск по всем полям:

```sql
SELECT *
```

При поисках не увеличивайте LIMIT по умолчанию более чем 10000, все данные по поиску буферизируются в браузере.

### Примеры оптимизации поисковых запросов

Пример 1:

```sql
SELECT SourceAddress, DestinationUserName, DeviceEventClassID 
FROM `events` 
WHERE DeviceProduct = 'Windows' 
AND NOT endsWith(SourceUserName,'$') AND SourceUserName != 'vasya' 
AND (startsWith(SourceUserName,'adm-') OR startsWith(DestinationUserName, 'adm_')) 
AND NOT inSubnet(SourceAddress, '10.10.10.150/24') AND NOT inSubnet(SourceAddress, '192.168.20.180/16') 
GROUP BY SourceAddress, DestinationUserName, eviceEventClassID
```

Оптимизированный запрос:

```sql
SELECT SourceAddress, DestinationUserName, DeviceEventClassID 
FROM `events` 
WHERE DeviceProduct = 'Windows' 
AND SourceUserName NOT LIKE '%$' AND SourceUserName != 'vasya' 
AND (SourceUserName LIKE 'adm-%' OR DestinationUserName LIKE 'adm_%') 
AND NOT inSubnet(SourceAddress, '10.10.10.150/24') AND NOT inSubnet(SourceAddress, '192.168.20.180/16') 
GROUP BY SourceAddress, DestinationUserName, eviceEventClassID
```

Или можно таже попробовать отказаться от функции inSubnet:

```sql
SELECT SourceAddress, DestinationUserName, DeviceEventClassID 
FROM `events` 
WHERE DeviceProduct = 'Windows' 
AND SourceUserName NOT LIKE '%$' AND SourceUserName != 'vasya' 
AND (SourceUserName LIKE 'adm-%' OR DestinationUserName LIKE 'adm_%') 
AND NOT (SourceAddress LIKE '10.10.10.%' OR SourceAddress LIKE '192.168.%.%') 
GROUP BY SourceAddress, DestinationUserName, eviceEventClassID
```

Принцип:

- Уменьшение количества условий: Объединяйте условия, где это возможно, чтобы сократить количество операций.
- Использование подзапросов или CTE: Если набор данных большой, рассмотрите возможность использования подзапросов или общих табличных выражений (CTE), чтобы разбить запрос на более мелкие, более управляемые части.
- Избегание функций в предложении WHERE: Такие функции, как endsWith, startsWith и inSubnet, могут быть узкими местами производительности. Если возможно, попробуйте переписать условия исравнить скорость выполнения.
- Функция ORDER BY тоже нагрузочная.

## Запросы к системным таблицам

У ClickHouse есть системные таблицы ([https://clickhouse.com/docs/en/operations/system-tables](https://clickhouse.com/docs/en/operations/system-tables)). Одна из них - query\_log ([https://clickhouse.com/docs/en/operations/system-tables/query\_log](https://clickhouse.com/docs/en/operations/system-tables/query_log)), содержит интересную информацию о запросах.

Чтобы обратиться к данным таблицами можно использовать клиент ClickHouse, который располагается по пути `/opt/kaspersky/kuma/clickhouse/bin/client.sh`

Можно заходить в консоль ClickHouse с помощью данного клиента, а также выполнять запросы через аргументы клиента, а также формировать запросы с помощью curl. В примерах ниже будет рассмотрен именно первый способ.

---

### Вывод запросов с сортировкой по длительности

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

```sql
SELECT *
FROM system.query_log
WHERE query_kind = 'Select' AND current_database = 'kuma' AND type = 'QueryFinish' AND event_date = '2024-01-22' 
AND query_duration_ms != 0
ORDER BY query_duration_ms DESC LIMIT 10
```

---

### Вывод запросов с сортировкой по использованной памяти

```sql
SELECT
	query_start_time,	
    query,
	query_duration_ms,
    formatReadableSize(memory_usage)
FROM system.query_log
WHERE current_database = 'kuma' AND query_kind != 'Insert' AND type = 'QueryFinish'
ORDER BY memory_usage DESC
LIMIT 10 \G;
```

---

### Вывод запросов, завершившихся ошибкой

```sql
SELECT *
FROM system.query_log
WHERE type IN ('ExceptionWhileProcessing', 'ExceptionBeforeStart') AND current_database = 'kuma'
LIMIT 10 \G;
```

# Создание переменных и функций в Dashboards

<p class="callout warning">**При дальнейшей настройке переменных используйте для них только уникальные значения (имена) для избежать дублирования, пересечения и конфликтов зависимостей**</p>

**Вы можете применять фильтрацию в случаях**

· Фильтрация данных на одном виджете (Использование локальных переменных)

· Фильтрация данных на нескольких виджетах в рамках одной панели мониторинга (Использование глобальных переменных)

#### **Для локальных переменных:**

В редактировании виджета раскройте меню доступных переменных

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/Coyimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/Coyimage.png)

Добавьте свою переменную, а в поле “название” вы можете поставить: категорию или значение или оставить пустым, чтобы в режиме просмотра панели мониторинга задавать значения.

Далее, в поле SQL-запрос в оператор WHERE добавьте функцию для работы с переменными.

Синтаксис для переменных с конкретным значением:

`filter(<название_поля_события> = $имя_переменной)`

Синтаксис для переменных с произвольным значением, которое задается вводом вручную:

`filter(<название_поля_события> like $имя_переменной)`

**например**

`<span lang="en-us"> SELECT count(ID) AS `metric` FROM `events` WHERE  DeviceAction = 'user login' and EventOutcome = 'succeeded' and filter(SourceUserName = $SourceName)</span>`

В примере локальная переменная <span lang="en-us">SourceName</span>, которая применяется в поле события <span lang="en-us">SourceName. </span>Далее сохраняем.

В режиме просмотра панели мониторинга в меню виджета нажмите на иконку фильтра

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/tcbimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/tcbimage.png)

В пустом поле укажите значение для переменной , далее нажмите кнопку “применить

Информация на дашборде автоматически обновляется. В данном случае видим сколько раз за последнюю неделю заходил пользователь в систему под учетной записью администратора

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/qQMimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/qQMimage.png)

#### **Для глобальных переменных:**

Необходимо перейти в редактирование панели мониторинга, далее “Настройки” для создания переменной

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/uaWimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/uaWimage.png)

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

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/sFpimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/sFpimage.png)

Добавьте свою переменную. Значения локальных и глобальных должны быть уникальными. Далее кнопка “Применить ”

Чтобы подключить глобальную переменную, перейдите в Редактирование виджета, Доступные переменные и выберите нужную, из списка созданных

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/bFOimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/bFOimage.png)

Аналогично запросу с локальными переменными, воспользуйтесь функцией <span lang="en-us">filter</span>(). В аргументе укажите нужное поле и присвойте ему значение переменной. **Например**<span lang="en-us">,</span>

`<span lang="en-us">SELECT count(ID) AS `metric` FROM `events` WHERE  DeviceAction = 'user login' and EventOutcome = 'succeeded' and filter(SourceUserName = $test)</span>`

Глобальная переменная <span lang="en-us">test</span> применяется к полю события <span lang="en-us">SourceName</span>. Сохраните изменения

Чтобы проверить работоспособность глобальных переменных, создайте стандартный, независимый от переменных виджет. **Например**, для отслеживания входов в систему различных пользователей

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/Mwoimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/Mwoimage.png)

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/djZimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/djZimage.png)

`<span lang="en-us">SELECT count(ID) AS `metric`, SourceUserName AS `value` FROM `events` WHERE Type = 4 AND DeviceAction = 'user login' and EventOutcome = 'succeeded' GROUP BY SourceUserName ORDER BY `metric` DESC LIMIT 250</span>`

Далее перейдите к параметру “Действия” внутри редактирования виджета

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/yZZimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/yZZimage.png)

 Вы можете настроить дальнейшие действия, как для взаимодействия с графиком, так и легендой

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/5y9image.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/5y9image.png)

Для применения фильтра выберите параметр “Фильтровать по переменной”. Сохраните виджет и панель мониторинга.

В режиме просмотра панели мониторинга нажмите на легенду/график в виджете Перейдите в события, если хотите посмотреть входы пользователя <span lang="en-us">admin</span> в систему за последнюю неделю в событиях

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/rRVimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/rRVimage.png)

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/A1jimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/A1jimage.png)

Или второй пункт, чтобы привязаться к переменной, станет <span lang="en-us">test</span>=<span lang="en-us">admin</span>

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/Qfcimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/Qfcimage.png)

Далее на панели мониторинга вы увидите появление глобального фильтра

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/pgmimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/pgmimage.png)

И автоматически все виджеты с привязкой к глобальной переменной адаптируют значения, видим сколько раз <span lang="en-us">admin</span> зашел в систему за неделю, также на виджетах появится значок фильтра.

Нажмите на него, чтобы увидеть более подробную информацию о фильтре

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/GEiimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/GEiimage.png)

Чтобы проверить, к каким виджетам применяется фильтр, повторите шаги из пункта создания глобальных переменных, проверьте параметр “Используется в виджетах”

[![image.png](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/scaled-1680-/Ofqimage.png)](https://kb.kuma-community.ru/uploads/images/gallery/2025-10/Ofqimage.png)