Часть задач готовыми отчётами не решается: нужны выборки под свой процесс, выгрузки во внешнюю систему, проверки по расписанию. Здесь собраны запросы, которые приходится писать чаще всего, — их можно выполнять в SQL-клиенте, встраивать в отчёты или вызывать из сценария компонентом «Запрос в базу данных».
oktell: таблицы настроек доступны в ней через представления, поэтому явно указывать oktell_settings обычно не требуется. Явное указание базы в примерах ниже оставлено там, где оно есть в исходных запросах.
Символы DTMF, набранные во время разговора
Задача возникает, когда абонент нажимает клавиши уже в разговоре и получить их из сценария нельзя. По цепочке коммутаций можно собрать всё набранное и, например, запустить служебное действие.
Используется таблица A_Stat_DTMF, входной параметр — idchain.
declare @s nvarchar(100)
set @s = ''
select @s = @s + symbol from A_Stat_dtmf where idchain = '00000000-0000-0000-0000-000000000000'
select @s
Для отчёта, где нужны сразу все цепочки, символы собираются в строку через FOR XML PATH:
select idchain,
(select [symbol] + ', '
FROM oktell..a_stat_dtmf sub_u
WHERE u.idchain = sub_u.idchain
FOR XML PATH (''))
FROM oktell..a_stat_dtmf u
GROUP BY idchain
Текущие статусы операторов
Возвращает актуальное состояние каждого оператора и время, когда это состояние было назначено. Полезно для дашбордов и внешних панелей.
SELECT u.id, u.NAME, h.state, h.TimeChange
FROM A_Users u
LEFT JOIN (
SELECT h.*
FROM (
SELECT UserId, max(TimeChange) TimeChange
FROM A_UserStateHistory
GROUP BY UserId
) t
JOIN A_UserStateHistory h ON t.UserId = h.UserId AND h.TimeChange = t.TimeChange
) h ON h.UserId = u.Id
WHERE u.id IN (
SELECT OperatorId
FROM [oktell_settings].[dbo].[A_TaskManager_Operators]
-- where TaskId = '…'
)
Раскомментированная строка ограничивает выборку операторами конкретной задачи.
Расшифровка кодов состояния
| Код | Состояние |
|---|---|
| 0 | Отключён |
| 1 | Готов |
| 2 | Перерыв |
| 3 | Нет на месте |
| 5, 6 | Занят |
| 7 | Без телефона |
Тот же запрос с расшифровкой и отделами — через временную таблицу; фильтры по пользователям и отделам оставлены закомментированными:
if exists (select * from tempdb..sysobjects where id = OBJECT_ID('tempdb..#u_cur_state'))
Drop table #u_cur_state
SELECT UserId, max(TimeChange) TimeChange
into #u_cur_state
FROM oktell.dbo.A_UserStateHistory
GROUP BY UserId
select a.name as "Имя",
a.state as "Код состояния",
CASE WHEN a.state = 1 THEN 'Готов'
WHEN a.state = 2 THEN 'Перерыв'
WHEN a.state in (5,6) THEN 'Занят'
WHEN a.state = 0 THEN 'Отключен'
WHEN a.state = 3 THEN 'Нет на месте'
WHEN a.state = 7 THEN 'Без телефона'
end as "Состояние",
a.grname as "Отдел"
from (
select au.name as "name",
(select top 1 h.state
from oktell.dbo.A_UserStateHistory as h
where h.UserId = ucs1.UserId AND h.TimeChange = ucs1.TimeChange
) as "state",
ag.name as "grname"
from #u_cur_state as ucs1
join oktell.dbo.a_users as au on ucs1.userid = au.id
left join oktell_settings.dbo.a_groups ag on au.parentgroupid = ag.id
) a
order by a.grname
Внутренний номер сотрудника по идентификатору
Какую задачу он решает
Пример понятнее определения. У менеджера был номер 800, в котором значился только он. Потом к номеру добавили несколько мобильных — и номер стал групповым. Если в системе есть, скажем, групповой номер операторской группы 100, то по обычному правилу «наименьший из групповых» номером менеджера начнёт считаться 100, а не 800.
Запрос находит именно 800. Логика такая:
- ищется номер, в котором может быть только один пользователь и сколько угодно внешних номеров и линий;
- если в номере больше одного пользователя или есть вложенные внутренние номера — номер считается групповым;
- если все номера сотрудника групповые — возвращается тот, у которого меньше объектов.
SELECT top 1 @prefix = Prefix
FROM (
SELECT COALESCE((SELECT count(*)
FROM A_RuleRecords s_r
LEFT JOIN A_NumberPlanAction s_npa ON s_r.ReactID = s_npa.NumID
LEFT JOIN A_RuleRecords ss_r ON s_npa.ExtraId = ss_r.RuleId
WHERE s_r.RuleID = r.RuleID
AND (s_r.ReactID IN (SELECT ID FROM A_USERS) OR s_npa.NumID IS NOT NULL)), 0) cnt
, np.Prefix, np.Visible
FROM A_NumberPlan np
INNER JOIN A_NumberPlanAction npa ON np.ID = npa.NumID
JOIN A_RuleRecords r ON r.RuleID = npa.ExtraId AND r.reactid = @userid
AND InnerAddressType = 0 -- только «Внутренние номера»
) t
ORDER BY cnt, Visible DESC
| Параметр | Направление | Что содержит |
|---|---|---|
@userid | Входной | Идентификатор пользователя |
@prefix | Выходной | Найденный внутренний номер |
Другие готовые запросы
В документации разработчика есть и другие типовые выборки, которые стоит знать до того, как писать своё:
| Запрос | Для чего |
|---|---|
| Распаковка номеров | Раскрыть групповой номер до состава входящих в него объектов |
| Получить список пользователей по внутреннему номеру | Обратная задача к предыдущей статье |
| Суммарное время нахождения абонента во Flash-буфере | Оценить, сколько абонент провёл на удержании |
| Распределение операторов по статусам | Сводка по текущим состояниям |
| Рабочее время операторов | Отработанное время за период |
| Входящие звонки по номерам, по задачам, по интервалам, за последние 30 дней, за сутки с разбивкой по 15 минут | Типовые выборки нагрузки |
| SL-уровень по задачам за текущий день | Уровень обслуживания |
| Формирование XML произвольной структуры | Подготовка данных для передачи во внешнюю систему |