Этот баннер — требование Роскомнадзора для исполнения 152 ФЗ.
«На сайте осуществляется обработка файлов cookie, необходимых для работы сайта, а также для анализа использования сайта и улучшения предоставляемых сервисов с использованием метрической программы Яндекс.Метрика. Продолжая использовать сайт, вы даёте согласие с использованием данных технологий».
Политика конфиденциальности
|
|
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
Есть процедурка, работает, ну очень долго. Работает с 3 таблицами, в которых более милиона записей, идет сложная групировка. Подскажите какие есть методы оптимизации процедуры ? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 23.04.2004, 16:55 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
Метод №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. Из известных проблем с процедурами. Зачастую, если в условии запроса используется переменная, выборка по индексу по данному полю не осуществляется. Избежать можно запихнув значение переменной во временную таблицу либо насильно указать использование индекса с помощью директив оптимизатора. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 23.04.2004, 18:40 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
Посмотреть, чему равно PDQ priority во время выполнения. Можно onstat-ом (ключ не помню). Если меня не обманывает мой склероз, PDQ используется тот, что был на момент компиляции, а не на момент выполнения. В таком вот аксепте ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 23.04.2004, 19:08 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
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; ********************** ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 23.04.2004, 19:19 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
1. Хотелось бы увидеть план запроса. 2. Сколько записей вставляется в итоге? 3. Сколько времени работает процедура? Может быть имеет смысл тюнить onconfig? На мой взгляд стоит разбить данный запрос на несколько мелких. OUTER, group by по CASE достаточно нагружабельны. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 23.04.2004, 19:37 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
вот план запроса 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 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 23.04.2004, 19:48 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
в итоге возвращается 53 записи процедура работает окколо 4-х часов ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 23.04.2004, 20:03 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
Т.е процедура ни при чем, запрос что в процедуре,что в dbaccess работает 4 часа ? В таком вот аксепте ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 23.04.2004, 21:25 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
Очень неплохо было бы попытаться упростить этот запрос разложением на последовательность более мелких. 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 команду, то это вовсе не значит, что она быстро выполнится, часто наоборот. Пусть процедура выглядит как целая программа с кучей переменных (в разумных пределах, конечно), зато это окупится наглядностью и, в конечном итоге, экономией времени при отладке. Успеха! ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 24.04.2004, 16:22 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
2 Julian Действительно без COUNT(DISTINCT...) и COUNT(...) запрос отработал намного быстрее ... В связи с этим вопрос по пункту 2.1 - внутри цикла делать SELECT только для MC а как с остальными таблицами? на каком этапе делать группировку ? Я сейчас сделал выборку в тмп таблицу всех записей по связке union.NACH_id=MD.CHARGE_ID и всех необходимых полей из MD Теперь планирую сделать Foreach c выборкой необходимых полей из других таблиц в переменные и вставкой в тмп1 таблицу - все это внутри одного Foreach. Затем из полученной таблицы получить данные с группировкой. Поправьте если что не так ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 26.04.2004, 16:44 |
|
||
|
Оптимизация процедур.....
|
|||
|---|---|---|---|
|
#18+
Да, лучше было бы написать: "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 -- если нет сортировки Надеюсь, что-нибудь пригодится... ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 27.04.2004, 01:04 |
|
||
|
|

start [/forum/topic.php?fid=44&fpage=66&tid=1609286]: |
0ms |
get settings: |
9ms |
get forum list: |
14ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
46ms |
get topic data: |
11ms |
get forum data: |
2ms |
get page messages: |
45ms |
get tp. blocked users: |
2ms |
| others: | 14ms |
| total: | 149ms |

| 0 / 0 |
