我有一个不稳定的pl / pgsql函数。这是来源:
DECLARE
l RECORD;
events_for_machine integer;
before_event "PRD".events_log;
machines_ids integer[];
island_controller RECORD;
before_order "PRD".events_log;
before_detail "PRD".events_log;
before_pallete "PRD".events_log;
before_operation "PRD".events_log;
timer timestamp;
timer2 timestamp;
BEGIN
machines_ids = string_to_array(machines_ids_g,',')::integer[];
for l in
select m.*
from
"PRD".machines m
inner join
unnest(machines_ids) n(id) on n.id = m.id
where
m.start_work_date < begin_date_g
order by m.id
LOOP
SELECT * INTO island_controller FROM "STRUCT".island_machines WHERE machine_id=l.id;
RAISE NOTICE 'pobieram zdarzenie before dla maszyny %',l.id;
SELECT * INTO before_event FROM "PRD".events_log WHERE plc_time < begin_date_g AND (((event_type_id IN (1,51) AND machine_id = island_controller.controller_id AND island_id = island_controller.island_id))
OR (event_type_id IN (2000,2001) AND machine_id=l.id)) ORDER BY plc_time DESC LIMIT 1;
IF before_event.plc_time IS NOT NULL THEN
RAISE NOTICE 'Getting info about first machine work time struct element';
RETURN QUERY SELECT * FROM "PRD".events_log WHERE event_type_id = 113 AND machine_id=l.id AND plc_time < before_event.plc_time ORDER BY plc_time DESC LIMIT 1;
RETURN QUERY SELECT * FROM "PRD".events_log WHERE event_type_id = 102 AND machine_id=l.id AND plc_time < before_event.plc_time ORDER BY plc_time DESC LIMIT 1;
RETURN QUERY SELECT * FROM "PRD".events_log WHERE event_type_id = 111 AND machine_id=l.id AND plc_time < before_event.plc_time ORDER BY plc_time DESC LIMIT 1;
RETURN QUERY SELECT * FROM "PRD".events_log WHERE event_type_id = 1010 AND machine_id=l.id AND plc_time < before_event.plc_time ORDER BY plc_time DESC LIMIT 1;
RETURN NEXT before_event;
END IF;
RAISE NOTICE 'generuje zdarzenia wlasciwe dla maszyny %',l.id;
RETURN QUERY SELECT * FROM "PRD".events_log WHERE
(event_type_id = ANY ('{1,51}'::integer[]) AND (machine_id=island_controller.controller_id AND island_id = island_controller.island_id) AND (plc_time BETWEEN begin_date_g AND end_date_g))
OR (event_type_id = ANY ('{2000,2001,107}'::integer[]) AND machine_id=l.id AND (plc_time >= begin_date_g AND plc_time <= end_date_g))
OR ((event_type_id = ANY ('{101,102,103,301,1010}'::integer[]) OR ((event_type_id >= 5000) AND (event_type_id <= 5999))) AND machine_id=l.id AND plc_time >= begin_date_g AND plc_time <= end_date_g) ORDER BY plc_time;
RAISE NOTICE 'koniec dla maszyny %',l.id;
END LOOP;
END;
有时函数执行时间约为9秒,对于相同的参数,有时约为40秒。它取决于什么?
什么可能是如此低效?
答案 0 :(得分:2)
不止一个问题。我不能说,出了什么问题,但是当它在缓存中找到必要的数据(postgresql,filesystem)时会很快,如果没有,则会很慢(并且取决于有多少循环)。
出了什么问题:
无用的加入
machines_ids = string_to_array(machines_ids_g,',')::integer[];
for l in
select m.*
from
"PRD".machines m
inner join
unnest(machines_ids) n(id) on n.id = m.id
你可以写
for l in
select *
from "PRD".machines m
where m.id = ANY(string_to_array(machines_ids_g,',')::integer[])
loop
很多重复的查询
RETURN QUERY SELECT * FROM "PRD".events_log WHERE event_type_id = 113 AND machine_id=l.id AND plc_time < before_event.plc_time ORDER BY plc_time DESC LIMIT 1;
RETURN QUERY SELECT * FROM "PRD".events_log WHERE event_type_id = 102 AND machine_id=l.id AND plc_time < before_event.plc_time ORDER BY plc_time DESC LIMIT 1;
RETURN QUERY SELECT * FROM "PRD".events_log WHERE event_type_id = 111 AND machine_id=l.id AND plc_time < before_event.plc_time ORDER BY plc_time DESC LIMIT 1;
RETURN QUERY SELECT * FROM "PRD".events_log WHERE event_type_id = 1010 AND machine_id=l.id AND plc_time < before_event.plc_time ORDER BY plc_time DESC LIMIT 1;
可以替换为
RETURN QUERY SELECT *
FROM "PRD".events
WHERE (event_type_id, plc_time) = (SELECT max(plc_time), event_type_id
FROM "PRD".events
WHERE event_type_id IN (113,102,111,1010)
GROUP BY event_type_id);
可能是复合索引(event_type_id,plc_time)应该有帮助
注意:
图案:
FOR n IN SELECT
SELECT .. WHERE ..=n
END FOR
当循环太多时,可能会很慢,并且在可能的情况下可以更好地处理一个查询