0


不安定な 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 秒の場合もあります。それは何に依存しますか?
何がそんなに非効率的でしょうか?

4

1 に答える 1

2

複数の問題があります。何が悪いのかはわかりませんが、キャッシュ (postgresql、ファイルシステム) に必要なデータが見つかった場合は高速になり、そうでない場合は遅くなります (ループの数によって異なります)。

なにが問題ですか:

無駄な参加

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

ループが多すぎると遅くなる可能性があり、可能な場合は1つのクエリで処理する方が良い

于 2012-11-27T13:06:24.197 に答える