powered by simpleCommunicator - 2.0.52     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Помогите оптимизировать функцию bvgCalcLCRByTask2
12 сообщений из 12, страница 1 из 1
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037380
BondVG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Здравствуйте, коллеги!
Есть функция, которая выводит отчет LCR - Показатель, отражающий процент звонков, не принятых по каким либо причинам.
Она выводит данные по временным интервалам. И вот понадобилось вывести данные за полгода с разбивкой по 5 мин.
Запрос выполнялся 1,5 часа. Помогите оптимизировать данную функцию.
Вот текст функции:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
USE [oktell]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:		Бондарев В.Г.
-- Create date: 15.01.2021
-- Description:	LCR - Показатель, отражающий процент звонков, не принятых по каким либо причинам.
-- Учитываются звонки с результатом:
-- 13 - Абонент прервал ожидание в очереди
-- Параметры:
-- @date1: Дата начала периода
-- @date2: Дата окончания периода
-- @time1: Начальное время
-- @time2: Конечное время
-- @interval: Интервал для расчета периодов:
--  1 - Все время (по умолчанию)
--  2 - По месяцам
--  3 - По дням
--  4 - По часам
--  5 - По 30 минут
--  6 - По 15 минут
--  7 - По 10 минут
--  8 - По 05 минут
-- @task_id: GUID идентификатор задачи.
-- @wait_term: Ожидание (условие):
--	1 - Больше или равно
--	2 - Меньше
-- @wait_time: Время ожидания, секунды
-- =============================================
ALTER   FUNCTION [dbo].[bvgCalcLCRByTask2] 
(
	-- Add the parameters for the function here
	@date1 datetime, @date2 datetime, @time1 datetime, @time2 datetime, @interval int, @task_id uniqueidentifier, @wait_term int, @wait_time int
)
RETURNS 
@Result TABLE 
(
	-- Add the column definitions for the TABLE variable here
	Id int Not Null Identity, DTStart datetime, DTStop datetime, IdTask uniqueidentifier, 
	CntTotal int not Null default 0, CntLost int not Null default 0, LCR decimal(18,2) not Null default 0.00,
	Unique(DTStart, Id)
)
AS
BEGIN

	If (@task_id is Null) return;

-- Генерация таблицы периодов
	Declare @intervals table (Id int not null identity, DTStart DateTime, DTStop DateTime, DStart DateTime, DStop DateTime, TStart DateTime, TStop DateTime, Unique(DTStart, Id))
	Insert Into @intervals
	Select * From oktell.dbo.GetDateTimeIntervals2(@date1, @date2, @time1, @time2, @interval)
--------------------------------------------------------------------------------------------------

	Set @date1 = CONVERT(date, @date1)
	Set @date2 = CONVERT(date, @date2)
	Set @time1 = CONVERT(time, @time1)
	Set @time2 = CONVERT(time, @time2)

	Declare @dtstart datetime = @date1+@time1, 
			@dtstop datetime  = @date2+@time2;
	
	if (@interval is null)	set @interval = 1;
	if (@wait_term is null) set @wait_term = 1
	if (@wait_time < 0)		set @wait_time = 0;

-- Выборка всей коммутационной информации входящих звонков по заданным параметрам
	Declare @lcr_tmp Table(
		[Id] int Not Null Identity,
		[IdProject] [uniqueidentifier] NOT NULL,
		[IdTask] [uniqueidentifier] NOT NULL,
		[IsOutput] [bit] NOT NULL,
		[IdEffort] [uniqueidentifier] NOT NULL,
		[IdOperator] [uniqueidentifier] NULL,
		[IdInList] [int] NOT NULL,
		[AbonentNumber] [nvarchar](20) NOT NULL,
		[CallResult] [int] NOT NULL,
		[CallResultInfo] [nvarchar](100) NOT NULL,
		[DateTimeStart] [datetime] NOT NULL,
		[DateStart] [datetime] NOT NULL,
		[TimeStart] [datetime] NOT NULL,
		[DateTimeStop] [datetime] NOT NULL,
		[IdConn] [uniqueidentifier] NULL,
		[IdChain] [uniqueidentifier] NULL,
		[IdAUser] [uniqueidentifier] NULL,
		[IdBUser] [uniqueidentifier] NULL,
		[IdExtLine] [uniqueidentifier] NOT NULL,
		[LenTime] [float] NOT NULL,
		[LenQueue] [float] NOT NULL,
		[IsBetweenOp] [bit] NULL,
		[OpponentInfo] [nvarchar](200) NULL,
		[IsHandled] [bit] NOT NULL,
		[IsSuccess] [bit] NOT NULL,
		[IsRecorded] [bit] NULL,
		[IdRecDir] [int] NULL,
		[ALineNum] [nvarchar](12) NULL,
		[BLineNum] [nvarchar](12) NULL,
		[UserResult] [int] NULL,
		Unique([DateTimeStart], [DateStart], [TimeStart], Id)
	)
	Insert Into @lcr_tmp
	Select /*Distinct */ec.* 
	from oktell_cc_temp..A_Cube_CC_EffortConnections as ec
	where (ec.DateStart between @date1 and @date2) and (ec.TimeStart between @time1 and @time2) 
			and  ec.IdTask = @task_id
			and ec.IsOutput < 1 and ec.IdChain is not null
	Order By ec.DateTimeStart
--------------------------------------------------------------------------------------------------

-- Формируем выходную таблицу
	Insert Into @Result ( DTStart, DTStop, IdTask )
	Select i.DTStart, i.DTStop, ec.IdTask 
	From @intervals as i
	Inner Join (
		Select IdTask, Min(DateTimeStart) as DTStart, Max(DateTimeStop) dtStop--, IdOperator
		From @lcr_tmp as ec
		Group By IdChain, IdTask--, IdOperator
	) as ec on ec.DTStart between i.DTStart and i.DTStop
	Group By i.DTStart, i.DTStop, ec.IdTask--, ec.IdOperator
	Order By i.DTStart

--------------------------------------------------------------------------------------------------

	declare @tblCount table ([Id] int Not Null Identity, DTStart DateTime, DTStop DateTime, Cnt int, 
		IdTask uniqueidentifier, Unique(DTStart, Id))--, IdOperator uniqueidentifier)
	-- Количество поступивших вызовов. Число цепочек по входящим задачам
	Insert Into @tblCount 
	Select i.DTStart, i.DTStop, Count(ec.cnt), IdTask--, IdOperator 
	From (
		Select IdChain, Count(IdChain) Over(Partition By IdTask) as cnt, IdTask, Min(DateTimeStart) dtStart, Max(DateTimeStop) dtStop--, IdOperator  
		From @lcr_tmp
		Group by IdChain, IdTask--, IdOperator
	) ec
	Inner join @intervals i on ec.dtStart between i.DTStart and i.DTStop
--	Inner join @intervals i on ec.dtStart >= i.DTStart and ec.dtStop <= i.DTStop
		Group By i.DTStart, i.DTStop, ec.IdTask--, ec.IdOperator
		Order By i.DTStart
--------------------------------------------------------------------------------------------------

	Update o
		Set CntTotal = tmp.Cnt
	From @Result o, @tblCount tmp
	Where o.DTStart = tmp.DTStart
		and (o.IdTask = tmp.IdTask)

	Delete From @tblCount
------------------------------------------------------------------------------------------------------
/*
[oktell_cc_temp].[dbo].[A_Cube_CC_Cat_TaskResultTypes] — типы результатов звонков по задачам

[Id] [int] NOT NULL — id типа результата
[Name] [nvarchar](500) NOT NULL — название типа результата звонка
1 - Неопределен
2 - Занято
3 - Не отвечает
4 - Недостаточное время обработки
5 - Успех
6 - Пропуск оператором
7 - Оператор отсутствует, пропустил обратный вызов по исходящей задаче
8 - Перезвонить позже
12 - Исключение номера при синхронизации
13 - Абонент прервал ожидание в очереди
15 - Отбой из дозванивающихся
16 - Исключение номера из обработки
17 - Исключение абонента из обработки
18 - Неуспех
19 - Отбой из очереди
20 - Удален оператором
21 - Входящий звонок. Превышено число подключений
22 - Входящий звонок вне периода активности
23 - Входящий звонок вне расписания
24 - Входящий звонок в неактивную задачу
25 - Превышено время ожидания в очереди
26 - Входящий звонок. Операторы отсутствует
27 - Входящий звонок. Операторы заняты
28 - Входящий звонок. Сбой или задача не найдена
29 - Перезвонить на другой номер
30 - Абонент не найден в таблице
31 - Обнаружен факс
*/
-- Выбираем все принятые звонки
	Declare @CallTaken table (IdChain uniqueidentifier)
	Insert Into @CallTaken
	Select ct.IdChain
	From (Select IdChain From @lcr_tmp ec
		Where IdChain is not null and  (ec.CallResult in (5, 8, 18, 29) or (ec.CallResult = 1 and ec.IsRecorded = 1))
		Group by IdChain
	) as ct

-- Удаляем все принятые звонки
	Delete From @lcr_tmp
	Where IdChain in (Select IdChain From @CallTaken)

	Delete @CallTaken
------------------------------------------------------------------------------------------------------

	Insert Into @tblCount 
	Select i.DTStart, i.DTStop, Count(ec.cnt), ec.IdTask 
	From (
		Select IdChain, Count(IdChain) Over(Partition By IdTask) as cnt, IdTask, Min(DateTimeStart) dtStart, Max(DateTimeStop) dtStop, Sum(LenQueue) LenQueue 
		From @lcr_tmp ec
		--where ec.LenTime != 0
		Group by IdChain, IdTask--, IdOperator
	) ec
	Inner join @intervals i on ec.dtStart between i.DTStart and i.DTStop
--	Inner join @intervals i on ec.dtStart >= i.DTStart and ec.dtStop <= i.DTStop
	Where ec.IdChain is not null
		and ((@wait_term = 1 and ec.LenQueue >= @wait_time) or (@wait_term = 2 and ec.LenQueue < @wait_time))
	Group By i.DTStart, i.DTStop, ec.IdTask--, IdOperator
	Order By i.DTStart

	Update o
	Set CntLost = tmp.Cnt, 
		LCR = case  
			when tmp.Cnt = 0 then 0.0
			when CntTotal = 0 then 0.0
			else Round(100 * Cast(tmp.Cnt as real)/CntTotal, 2)
			end
	From @Result o, @tblCount tmp
	Where o.DTStart = tmp.DTStart
		and (o.IdTask = tmp.IdTask)


	RETURN 
END




В ней используется функция генерации таблицы интервалов oktell.dbo.GetDateTimeIntervals2(@date1, @date2, @time1, @time2, @interval). Она работает довольно быстро - около 1 сек.

Буду очень признателен всем за помощь.
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037385
Чтобы сказать что-то конкретное нужные актуальные планы выполнения. Могу предложить банально впихнуть на каждый стейтмент OPTION(RECOMPILE) (хуже все равно не будет) чтобы оценка строк была на "1" а более адекватная. Плюс если хочется чтобы использовался параллелизм то временные таблицы ваш выбор.
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037389
Фотография alexeyvg
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BondVG
Запрос выполнялся 1,5 часа. Помогите оптимизировать данную функцию.
Циклов нет, значит, просто какой то запрос (или несколько) выполняются долго.
Настройте профайлер с выводом актуальных планов на конкретный SPID из окна SSMS, и запустите в окне процедуру.
Сразу получите ценную количественную информацию по выполнению всех запросов, и их планы выполнения.

Ещё, сразу вызывает подозрение связывание по DTStart, IdTask (например, таблицы @Result и @tblCount ), в то время как ПК в этих таблицах - DTStart, Id. Зачем там Id, как оно используется?
Притом в @tblCount вставка делается с уникальностью DTStart, DTStop, IdTask. Почему туда затесалось DTStop, а в связке её нет?
Нет ли у вас там скрытого кросс-джойна?
Это будет хорошо видно из результатов трассировки, по количеству обработанных записей.
В общем, с логикой функции нужно поработать.
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037400
invm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BondVG
Код: sql
1.
Inner join @intervals i on ec.dtStart between i.DTStart and i.DTStop

Гарантированный Nested Loops.
Учитывая, что для озвученных условий в @intervals около 50000 строк и нет подходящего индекса, потенциально будете иметь проблемы быстродействия, если в левой таблице этого соединения будет много строк.

ЗЫ: Обычно подобные задачи решаются в один запрос, без генерации интервалов. Но озвучить задачу в юзабельном виде забыли.
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037405
BondVG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
alexeyvg
Зачем там Id, как оно используется?

Id используется для создания индекса по столбцу DTStart. Значение DTStart может быть неуникально, если собирать данные по нескольким задачам (IdTask).

Притом в @tblCount вставка делается с уникальностью DTStart, DTStop, IdTask. Почему туда затесалось DTStop, а в связке её нет?
Возможно, DTStop здесь лишнее. Посмотрю...

Нет ли у вас там скрытого кросс-джойна?
Да вроде нет.
Спасибо, я попробую настроить профайлер.
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037407
BondVG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
invm
BondVG
Код: sql
1.
Inner join @intervals i on ec.dtStart between i.DTStart and i.DTStop

Гарантированный Nested Loops.
Учитывая, что для озвученных условий в @intervals около 50000 строк и нет подходящего индекса, потенциально будете иметь проблемы быстродействия, если в левой таблице этого соединения будет много строк.

Индекс создается вот этим ограничением: Unique(DTStart, Id).

ЗЫ: Обычно подобные задачи решаются в один запрос, без генерации интервалов. Но озвучить задачу в юзабельном виде забыли.
Не могли бы Вы показать пример одного запроса для моей задачи.
Нужно, чтобы данные выводились вот так:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
Id	DTStart	DTStop	IdTask	CntTotal	CntLost	LCR
1	2020-12-01 08:00:00.000	2020-12-01 08:05:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	3	0	0.00
2	2020-12-01 08:10:00.000	2020-12-01 08:15:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	2	0	0.00
3	2020-12-01 08:15:00.000	2020-12-01 08:20:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	1	0	0.00
4	2020-12-01 08:20:00.000	2020-12-01 08:25:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	2	0	0.00
5	2020-12-01 08:25:00.000	2020-12-01 08:30:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	3	0	0.00
6	2020-12-01 08:30:00.000	2020-12-01 08:35:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	4	0	0.00
7	2020-12-01 08:35:00.000	2020-12-01 08:40:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	7	1	14.29
8	2020-12-01 08:40:00.000	2020-12-01 08:45:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	4	0	0.00
9	2020-12-01 08:45:00.000	2020-12-01 08:50:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	2	0	0.00
10	2020-12-01 08:50:00.000	2020-12-01 08:55:00.000	EF2F8DBE-BBDF-46F0-8F05-2990EF850B3C	1	0	0.00
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037441
invm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BondVG
Индекс создается вот этим ограничением: Unique(DTStart, Id).
Из этого индекса нельзя взять DTStop, поэтому он бесполезен. Как минимум, должно быть unique clustered.
BondVG
Не могли бы Вы показать пример одного запроса для моей задачи.
Для Вашей не могу - просто лень.
Ибо Вы не подготовили ни исходных данных, ни схем таблиц и индексов, ни внятного описания задачи.
Могу просто пример показать:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
declare @t table (dt datetime);
declare @base_dt datetime = '20210101', @interval_in_minutes int = 5;

insert into @t
 (dt)
 select top (100000)
  dateadd(minute, rand(checksum(newid())) * 365 * 24 * 60, @base_dt) 
 from
  master.dbo.spt_values a cross join
  master.dbo.spt_values b;

select
 a.dt_start, dateadd(minute, @interval_in_minutes, a.dt_start) as dt_end, count(*)
from
 @t t cross apply
 (select dateadd(minute, (datediff(minute, '1900', t.dt) / @interval_in_minutes) * @interval_in_minutes, '1900')) a(dt_start)
group by
 a.dt_start
order by
 a.dt_start;
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037478
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1. Аффтор ленивец - даже не удосужился выяснить какой из запросов функции занимает больше всего времени.

2. Любовь к группировкам сгубила многих. АффторЪ тоже весьма грешен. 4 (четыре) однотипных группировки одной и той же таблицы (самой большой).
Зачем?
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037507
BondVG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aleks222
1. Аффтор ленивец - даже не удосужился выяснить какой из запросов функции занимает больше всего времени.

2. Любовь к группировкам сгубила многих. АффторЪ тоже весьма грешен. 4 (четыре) однотипных группировки одной и той же таблицы (самой большой).
Зачем?


Вот, как раз выясняю.
Что Вы предлагаете использовать вместо группировок?
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037553
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BondVG
aleks222
1. Аффтор ленивец - даже не удосужился выяснить какой из запросов функции занимает больше всего времени.

2. Любовь к группировкам сгубила многих. АффторЪ тоже весьма грешен. 4 (четыре) однотипных группировки одной и той же таблицы (самой большой).
Зачем?


Вот, как раз выясняю.
Что Вы предлагаете использовать вместо группировок?


Вестимо группировку, только одну.
Заодно три соединения станут ненужными.
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40037674
BondVG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Переписал функцию в виде процедуры, переделал все таблицы во временные с индексами.
Как результат, запрос стал выполняться за 9 минут.

Спасибо всем за помощь.

alex222
Вестимо группировку, только одну.
Заодно три соединения станут ненужными.


Я не понимаю, как можно использовать только одну группировку. Ведь эти группировки хоть и однотипные, но используются для разных условий. Можете подсказать?
...
Рейтинг: 0 / 0
Помогите оптимизировать функцию bvgCalcLCRByTask2
    #40038985
BondVG
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aleks222

Вестимо группировку, только одну.
Заодно три соединения станут ненужными.

Я сообразил как избавиться от вложенных запросов и лишних группировок.
Теперь запрос выполняется за 25 секунд.

Друзья, всем спасибо за помощь.
...
Рейтинг: 0 / 0
12 сообщений из 12, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Помогите оптимизировать функцию bvgCalcLCRByTask2
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]