powered by simpleCommunicator - 2.0.59     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Informix [игнор отключен] [закрыт для гостей] / Оптимизация процедур.....
11 сообщений из 11, страница 1 из 1
Оптимизация процедур.....
    #32494701
LOTOS
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть процедурка, работает, ну очень долго. Работает с 3 таблицами, в которых более милиона записей, идет сложная групировка. Подскажите какие есть методы оптимизации процедуры ?
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32494967
Фотография Daugava
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Метод №1.
set explain on;
update statistics for procedure <name>;

Смотреть планы запросов процедуры в sqexplain.out, думать над их улучшением.

Метод №2.
После каждого запроса сохранять текущее время в протоколе. В текстовом через "system 'date >>/tmp/myproc.prt" или в таблице с помощью unittime из
select sh_curtime
from sysmaster:sysshmvals (обычный current не подойдет, так как во время выполнения процедуры он не меняется).

Отловить самую длительную часть и мучать ее согласно Методу №1.

Addon.
Из известных проблем с процедурами. Зачастую, если в условии запроса используется переменная, выборка по индексу по данному полю не осуществляется. Избежать можно запихнув значение переменной во временную таблицу либо насильно указать использование индекса с помощью директив оптимизатора.
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32495017
Выбегалло
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Посмотреть, чему равно PDQ priority во время выполнения. Можно onstat-ом (ключ не помню). Если меня не обманывает мой склероз, PDQ используется тот, что был на момент компиляции, а не на момент выполнения.


В таком вот аксепте
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32495025
LOTOS
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
PDQPRIORITY = 100
вот приблизительные размеры таблиц
MTR_DETAILS_1, MTR_CRED_DETAILS ~ по 10 млн. записей
union ~ 1 млн. записей
mtr_par_rating 200 000

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

вот злополучный кусок который все тормозит ...
********************
INSERT INTO USLUG
SELECT rusnam,
gos.name,
start,
end,
CASE
WHEN MPR.MTR_DIRECTION_ID[1,3]='703'
THEN 'Iaea?.naycu(703)'
WHEN MPR.MTR_DIRECTION_ID[1,3]='800'
THEN 'Oneoaa 800'
WHEN MPR.MTR_DIRECTION_ID[1,3]='900'
THEN 'Aoaeioaen 900'
WHEN MPR.MTR_ZONE_ID = 6
AND mntr_flag = 0
THEN 'Nioiaay naycu'
ELSE CASE WHEN MPR.CALL_KIND_ID=1
THEN 'Aaoiiaoeea '
WHEN MPR.CALL_KIND_ID=2
THEN 'Caeac. ?acaiai?u'
END
END USL,
mpr.client_class_id,
COUNT(*) AS COUN,
SUM(CASE WHEN mpr.mtr_direction_id[1,3] = '100' THEN 0 ELSE md.SUMMA END) AS SU,
COUNT(DISTINCT MD.CLIENT_ID) AS ABON,
sum(md.duration+md.add_service_count) as time,
SUM(CASE WHEN mpr.mtr_direction_id[1,3] = '100' THEN 0 ELSE NVL(mc.credit,0) END) AS CRED,
SUM(CASE WHEN mpr.mtr_direction_id[1,3] = '100' THEN 0 ELSE
CASE WHEN mc.credit IS NULL THEN 0 ELSE 1 END END) AS talk_cred,
0, 0, 0, 0
FROM MTR_PAR_RATING MPR, MTR_DETAILS_1 MD,
gos, union, outer MTR_CRED_DETAILS MC

WHERE union.NACH_id=MD.CHARGE_ID
AND gos.TELEPH_STATION_ID=MD.ATS_ID
AND MPR.MTR_PAR_RATING_ID=MD.MTR_PAR_RATING_ID
and mpr.replflag>=0
and md.replflag>=0
AND MPR.UNIT_USE_KIND_ID<>9
AND md.traffic_reg_id = 1
AND DATE(md.event_date) BETWEEN gos.start_date AND gos.finish_date - 1 UNITS DAY
AND MC.CHARGE_ID = MD.CHARGE_ID
and mc.replflag>=0
GROUP BY 1,2,3,4,5,6;
**********************
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32495038
Фотография Daugava
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1. Хотелось бы увидеть план запроса.
2. Сколько записей вставляется в итоге?
3. Сколько времени работает процедура? Может быть имеет смысл тюнить onconfig?

На мой взгляд стоит разбить данный запрос на несколько мелких. OUTER, group by по CASE достаточно нагружабельны.
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32495045
LOTOS
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
вот план запроса

insert into "lbadmin".uslug (rus_id,name,start,end,usl,class,talk,summa,abon,time,credit,talk_cred,free_talk,free_user,free_time,free_summa) select ? ,x2.name ,? ,? ,CASE WHEN (x0.mtr_direction_id [1,3] = '703' ) THEN '...........(703)' WHEN (x0.mtr_direction_id [1,3] = '800' ) THEN '...... 800' WHEN (x0.mtr_direction_id [1,3] = '900' ) THEN '......... 900' WHEN ((x0.mtr_zone_id = 6 ) AND (x0.mntr_flag = 0 ) ) THEN '....... .....' ELSE CASE WHEN (x0.call_kind_id = 1 ) THEN '.......... ' WHEN (x0.call_kind_id = 2 ) THEN '...... .........' END END ,x0.client_class_id ,count(*) ,sum(CASE WHEN (x0.mtr_direction_id [1,3] = '100' ) THEN 0 ELSE x1.summa END ) ,count(distinct x1.client_id ) ,sum((x1.duration + x1.add_service_count ) ) ,sum(CASE WHEN (x0.mtr_direction_id [1,3] = '100' ) THEN 0 ELSE NVL (x4.credit ,0 ) END ) ,sum(CASE WHEN (x0.mtr_direction_id [1,3] = '100' ) THEN 0 ELSE CASE WHEN (x4.credit IS NULL ) THEN 0 ELSE 1 END END ) ,0 ,0 ,0 ,0 from "lbadmin".mtr_par_rating x0 ,"lbadmin".mtr_details_1 x1 ,"lbadmin".gos x2 ,"lbadmin".union x3 ,outer("lbadmin".mtr_cred_details x4 ) where ((((((((((x3.nach_id = x1.charge_id ) AND (x2.teleph_station_id = x1.ats_id ) ) AND (x0.mtr_par_rating_id = x1.mtr_par_rating_id ) ) AND (x0.replflag >= 0 ) ) AND (x1.replflag >= 0 ) ) AND (x0.unit_use_kind_id != 9 ) ) AND (x1.traffic_reg_id = '1' ) ) AND ((DATE (x1.event_date ) >= x2.start_date ) AND (DATE (x1.event_date ) <= (x2.finish_date - 1 UNITS day ) ) ) ) AND (x4.charge_id = x1.charge_id ) ) AND (x4.replflag >= 0 ) ) group by 1 ,x2.name ,3 ,4 ,5 ,x0.client_class_id

QUERY:
------


Estimated Cost: 2
Estimated # of Rows Returned: 11


Temporary Files Required For: Group By

1) lbadmin.mpr: SEQUENTIAL SCAN

Filters: (lbadmin.mpr.replflag >= 0 AND lbadmin.mpr.unit_use_kind_id != 9 )

2) lbadmin.md: INDEX PATH

Filters: (lbadmin.md.replflag >= 0 AND lbadmin.md.traffic_reg_id = '1' )

(1) Index Keys: mtr_par_rating_id charge_id summa client_id
Lower Index Filter: lbadmin.md.mtr_par_rating_id = lbadmin.mpr.mtr_par_rating_id
NESTED LOOP JOIN

3) lbadmin.gos: INDEX PATH

Filters: DATE (lbadmin.md.event_date ) <= lbadmin.gos.finish_date - 1 UNITS day

(1) Index Keys: teleph_station_id start_date (Serial, fragments: ALL)
Lower Index Filter: lbadmin.gos.teleph_station_id = lbadmin.md.ats_id
Upper Index Filter: lbadmin.gos.start_date <= DATE (lbadmin.md.event_date )
NESTED LOOP JOIN

4) lbadmin.union: INDEX PATH

(1) Index Keys: nach_id (Key-Only) (Serial, fragments: ALL)
Lower Index Filter: lbadmin.union.nach_id = lbadmin.md.charge_id
NESTED LOOP JOIN

5) lbadmin.mc: INDEX PATH

Filters: lbadmin.mc.replflag >= 0

(1) Index Keys: charge_id credit
Lower Index Filter: lbadmin.mc.charge_id = lbadmin.md.charge_id
NESTED LOOP JOIN
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32495051
LOTOS
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
в итоге возвращается 53 записи
процедура работает окколо 4-х часов
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32495100
Выбегалло
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Т.е процедура ни при чем, запрос что в процедуре,что в dbaccess работает 4 часа ?


В таком вот аксепте
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32495420
Julian
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Очень неплохо было бы попытаться упростить этот запрос
разложением на последовательность более мелких.

1) обычно сильно тормозит COUNT(DISTINCT...). Так ли это в данном
случае, легко проверить - вместо него поставь какую-нибудь
константу и повтори запрос (процедуру) как есть, ничего больше
не меняя.
2) замедляет и OUTER, особенно при склейке нескольких больших
таблиц.

Исходя из этого, по-моему, лучше выполнить SELECT отдельно, для
начала из FROM исключив OUTER MC, а потом сделать INSERT.
Варианты:
2.1) FOREACH SELECT INTO переменные,
внутри цикла отдельным SELECT сходить в MC за нужными значениями
2.2) SELECT INTO TEMP TABLE T1,
SELECT FROM TTT, OUTER MC
В обоих случаях вместо COUNT(DISTINCT MD.CLIENT_ID) поставить
просто MD.CLIENT_ID.
В обоих случаях результат поместить в TEMP TABLE T2, созданную
в начале процедуры и полями соответствующую полям, которые нужно
вставить в в USLUG. И потом сделать
INSERT INTO USLUG ( имена_полей )
SELECT T2.*, COUNT(DISTINCT T2.CLIENT_ID)
Здесь список имена_полей хорошо ставить чисто методологически,
чтобы в будущем не болела голова, если в таблице добавится или
уберется какое-либо поле.

3) кроме того, четко увидишь - SELECT тормозит или INSERT в
исходном варианте INSERT INTO USLUG SELECT ...
Ведь может быть в USLUG куча триггеров и/или индексов, а также
текущих блокировок, а это совсем другая песня.
Конечно, при 53 выбираемых эаписях это, скорее всего, не так,
но я о методике.

4) если скорость выполнения процедуры повысится, можно пойти дальше:
[внутри FOREACH] сделать еще одну ступеньку: выбирать сначала
не из 4-х склеенных таблиц (без outer mc), а из двух или трех
(каких - решаешь, зная свою БД и содержание таблиц) - во
временную T0, а потом выбираешь FROM T0,+остальные_таблицы(а).
Желательно, чтобы результатом первой ступеньки было как можно
меньше записей.

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

Успеха!
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32497196
LOTOS
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
2 Julian

Действительно без COUNT(DISTINCT...) и COUNT(...) запрос отработал намного быстрее ... В связи с этим вопрос по пункту 2.1 - внутри цикла делать SELECT только для MC а как с остальными таблицами? на каком этапе делать группировку ?
Я сейчас сделал выборку в тмп таблицу всех записей по связке
union.NACH_id=MD.CHARGE_ID и всех необходимых полей из MD
Теперь планирую сделать Foreach c выборкой необходимых полей из других таблиц в переменные и вставкой в тмп1 таблицу - все это внутри одного Foreach.
Затем из полученной таблицы получить данные с группировкой.
Поправьте если что не так
...
Рейтинг: 0 / 0
Оптимизация процедур.....
    #32497705
Julian
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, лучше было бы написать:
"2.1) FOREACH SELECT INTO переменные,
И ТАМ ЖЕ внутри цикла отдельным SELECT ..."
т.е. речь шла об избавлении от OUTER MC по такой схеме:

FOREACH
SELECT ..., md.charge_id INTO ..., v_charge_id_md
FROM MPR, MD, GOS, UNION
WHERE ...
GROUP BY ...

SELECT mc.credit INTO v_charge_id_mc
FROM MC
WHERE mc.charge_id = v_charge_id_md

RETURN переменные WITH RESUME;
END FOREACH

Т.е. не обратил внимания, что mc.credit стоит внутри SUM(). Но все равно суть
такая - разложение select-а на составляющие.

В общем, я имел ввиду именно то, что Вы сейчас и делаете (похоже на п.4).

Еще несколько субъективных замечаний.

COUNT(*) здесь на скорость практически не влияет (DISTINCT - да, а просто -
нет), тем более, что все равно рядом стоят SUM().

Подстроку MPR.MTR_DIRECTION_ID[1,3], конечно, лучше вычислить один раз, а в
исходном примере она может вычисляться до 5 раз на каждой записи из MPR.
На большом количестве записей это неправильно.

Ф-ция NVL(mc.credit,0) не долго ли работает и не вызывается ли слишком часто
(т.е. как много записей с mpr.mtr_direction_id[1,3] <> '100'?). Проверьте и это,
возможно, процентов 10% скорости она отъедает, и Вы найдете способ обойтись
без нее.

Как избавиться от DISTINCT, если посчитать уникальные CLIENT_ID все-таки нужно?
Раз уж все равно делаете
FOREACH
SELECT client_id, ... INTO v_client_id
FROM MD, UNION,
то добавьте:
IF v_client_id = previous_client_id THEN
LET v_c = 0;
ELSE
LET v_c = 1;
LET previous_client_id = v_client_id;
-- и перед FOREACH, конечно: LET previous_client_id = -1
END IF
INSERT INTO TMP1 (..., next_client_flag) VALUES( ..., v_c );
END FOREACH
Тогда при SELECT из TMP1 ниже вместо COUNT(DISTINCT CLIENT_ID) можно
поставить SUM(next_client_flag) и тем самым Informix будет избавлен от
необходимости делать кучу сортировок.

И еще иногда дает эффект в FOREACH:
вместо
AND DATE(md.event_date) BETWEEN gos.start_date AND gos.finish_date - 1 UNITS DAY
сделать
AND DATE(md.event_date) > gos.start_date
а внутри цикла
IF v_event_date > gos.finish_date - 1 UNITS DAY THEN
EXIT FOREACH -- если есть сортировка по event_date (попробуйте добавить,
-- м.б. записи в MD у вас идут почти последовательно, и ORDER BY
-- все равно нужен?)
или CONTINUE FOREACH -- если нет сортировки

Надеюсь, что-нибудь пригодится...
...
Рейтинг: 0 / 0
11 сообщений из 11, страница 1 из 1
Форумы / Informix [игнор отключен] [закрыт для гостей] / Оптимизация процедур.....
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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