Готовые SQL-запросы

Часть задач готовыми отчётами не решается: нужны выборки под свой процесс, выгрузки во внешнюю систему, проверки по расписанию. Здесь собраны запросы, которые приходится писать чаще всего, — их можно выполнять в 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
Таблица наполняется только при включённой настройке Символы DTMF попадают в базу, лишь если в разделе «Администрирование → Общие настройки → Управление базами данных» включён параметр «Сохранять в БД все получаемые по внешним линиям DTMF-символы». Если он выключен, запрос отработает и вернёт пусто — без всякого признака того, что дело в настройке.

Текущие статусы операторов

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

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

Внутренний номер сотрудника по идентификатору

Начиная с версии 2.10 запрос не нужен Внутренний номер пользователя получают компонентом «Статус объекта»: тип объекта «Пользователь», функция «Внутренний номер». Запрос ниже остаётся актуальным для более ранних версий и для случаев, когда выборка нужна вне сценария.

Какую задачу он решает

Пример понятнее определения. У менеджера был номер 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 произвольной структурыПодготовка данных для передачи во внешнюю систему
Осторожно с тяжёлыми запросами на рабочем сервере База Октелл обслуживает работающую телефонию. Выборки за большие периоды, курсоры и построения по всем цепочкам коммутаций способны заметно нагрузить сервер. Такие запросы разумно выполнять в часы наименьшей нагрузки, а регулярные выгрузки строить на отдельной копии базы.