PostgreSQL 提高 PL/pgSQL 函数的性能

Posted

技术标签:

【中文标题】PostgreSQL 提高 PL/pgSQL 函数的性能【英文标题】:PostgreSQL increasing the performance of a PL/pgSQL Function 【发布时间】:2014-03-24 05:13:21 【问题描述】:

我有一个 PostgreSQL 函数脚本,用于根据作为参数传递给函数的记录数来模拟某些表。执行脚本时发生的情况是它必须检查现有记录然后插入,即增量加载,因此花费的时间非常长。如何提高此脚本的性能?我把我的脚本放在这里:

CREATE OR REPLACE FUNCTION ccdb.perf_test_new(records bigint)
  RETURNS void AS
$BODY$

DECLARE 
i bigint;
count bigint;
bill_mnth integer;
j integer;
cin_num bigint;
BEGIN

count := records;
bill_mnth := 201400;


FOR i IN 1..count
LOOP


INSERT INTO ccdb.consumer_index_details_new
    (
        cin
        ,pole_lmdt
        ,meter_lmdt
        ,dtr_lmdt
        ,r_apdrp_town_flag
        ,town
        ,creation_dt
        ,created_by
    )
SELECT nextval('ccdb.cin_gen')
       ,now()
       ,now()
       ,now()
       ,1
       ,'Kottayam'
       ,now()
       ,'System';



INSERT INTO ccdb.consumers_new
    (   
    cin
    ,consumer_num
    ,first_name
    ,org_unit_id
    ,source_system_flag
    ,cust_area_type
    ,cons_cat_flag
    ,legacy_consumer_num
    ,cust_connection_id
    ,purpose_id
    ,tariff_id
    ,connected_load
    ,connected_load_uom
    ,consumer_phase
    ,supply_voltage
    ,connection_date
    ,bill_freq_id
    ,conn_status
    ,conn_category_group_id
    ,conn_cat_subgroup_id
    ,conn_address
    ,billing_address_1
    ,billing_address_dist
    ,permanent_address
    ,communication_address
    ,conn_owner_address
    ,pricing_type_flag
    ,conn_owner_customer_id
    ,ownership_flag
    ,district_id
    ,sanction_load
    ,sanction_load_uom
    ,advance_amount
    ,total_arrear
    ,dc_date
    ,creation_dt
    ,created_by
    ,cdemand_unit
    )
SELECT 
    ci.cin
    ,(SELECT CASE WHEN MAX(consumer_num) IS NULL THEN 1146341200001 ELSE ((MAX(consumer_num))+1) END FROM ccdb.consumers_new)
    ,'Perf_Test'||i
    ,4634
    ,1
    ,2
    ,1
    ,16876
    ,(SELECT CASE WHEN MAX(cust_connection_id) IS NULL THEN 1146340000001 ELSE ((MAX(cust_connection_id))+1) END FROM ccdb.consumers_new)
    ,15
    ,1
    ,800
    ,'W'
    ,1
    ,300
    ,now()
    ,2
    ,1
    ,1
    ,1001
    ,'SAPEER MANZIL,,KARAPUZHA P O,,KOTTAYAM,'
    ,'SAPEER MANZIL,,KARAPUZHA P O,,KOTTAYAM,'      
    ,'KOTTAYAM'
    ,'SAPEER MANZIL,,KARAPUZHA P O,,KOTTAYAM,'
    ,'SAPEER MANZIL,,KARAPUZHA P O,,KOTTAYAM,'
    ,'SAPEER MANZIL,,KARAPUZHA P O,,KOTTAYAM,'
    ,1
    ,(SELECT CASE WHEN MAX(conn_owner_customer_id) IS NULL THEN 1146340001 ELSE ((MAX(conn_owner_customer_id))+1) END FROM ccdb.consumers_new)
    ,1
    ,5
    ,600
    ,'W'
    ,45
    ,3
    ,now()
    ,now()
    ,'System'
    ,'KVA'
FROM ccdb.consumer_index_details_new ci
WHERE cin > (SELECT MAX(cin) FROM ccdb.consumers_new);


SELECT MAX(cin) INTO cin_num FROM ccdb.consumers_new;


FOR j IN 1..5
LOOP



    INSERT INTO ccdb.bills_new
                (
                    bill_id
                    ,source_system_id
                    ,mbc_bill_id
                    ,mbc_bill_no
                    ,cin
                    ,cust_connection_id
                    ,consumer_number
                    ,cust_type_flag
                    ,bill_type_group_code
                    ,bill_type_code
                    ,bill_month
                    ,total_consumption
                    ,bill_date
                    ,due_date
                    ,dc_date
                    ,org_unit_id
                    ,parent_bill_id
                    ,category_flag
                    ,status_flag
                    ,conn_cat_subgroup_id
                    ,dispute_flag
                    ,inst_flag
                    ,approved_date
                    ,bill_amt
                    ,paid_amt
                    ,creation_dt
                    ,created_by
                )
                SELECT
                    nextval('ccdb.bills_seq')
                    ,1
                    ,(SELECT CASE WHEN MAX(mbc_bill_id) IS NULL THEN 1000000001 ELSE (MAX(mbc_bill_id))+1 END FROM ccdb.bills_new)
                    ,(SELECT CASE WHEN MAX(mbc_bill_no) IS NULL THEN 4634000000001 ELSE (MAX(mbc_bill_no))+1 END FROM ccdb.bills_new)
                    ,c.cin
                    ,c.cust_connection_id
                    ,c.consumer_num
                    ,1
                    ,'EB'
                    ,'RgCC'
                    ,bill_mnth+j
                    ,400
                    ,now()
                    ,now()
                    ,now()
                    ,4634
                    ,currval('ccdb.bills_seq')
                    ,1
                    ,3
                    ,c.conn_cat_subgroup_id
                    ,0
                    ,0
                    ,now()
                    ,1000
                    ,700
                    ,now()
                    ,'System'
                FROM ccdb.consumers_new c
                WHERE c.cin = cin_num;


        INSERT INTO ccdb.bill_head_details_new
        (
            bill_id
            ,charge_head_code
            ,amount_billed
            ,amount_paid
            ,ccdb_update_time
            ,creation_dt
            ,created_by
            ,tariff_id
            ,demand_date
        )
        SELECT
            bill_id
            ,'EB'
            ,1000
            ,700
            ,now()
            ,now()
            ,'System'
            ,1
            ,now()
        FROM ccdb.bills_new
        WHERE bill_id > (SELECT MAX(bill_id) FROM ccdb.bill_head_details_new);


END LOOP;



INSERT INTO ccdb.payments_new
            (
                payment_id
                ,receipt_id
                ,source_system_flag
                ,cin
                ,consumer_nbr
                ,cust_connection_id
                ,cust_type_flg
                ,receipt_type_id
                ,mop_code
                ,coll_effect_date
                ,coll_entry_date
                ,receipt_num
                ,receipt_amt
                ,receipt_loc_flg
                ,receipt_date
                ,cancel_flag
                ,acc_type_id
                ,cust_section_code
                ,coll_section_code
                ,remarks
                ,pm_paydate
                ,pm_amount
                ,creation_dt
                ,created_by
            )
SELECT 
    nextval('ccdb.payments_seq')
    ,'REC/35747/2013'
    ,1
    ,c.cin
    ,c.consumer_num
    ,c.cust_connection_id
    ,1
    ,27
    ,2
    ,now()
    ,now()
    ,(SELECT CASE WHEN MAX(CAST(receipt_num AS bigint)) IS NULL THEN 102820110000001 ELSE (MAX(CAST(receipt_num AS bigint)))+1 END FROM ccdb.payments_new)
    ,700
    ,1
    ,now()
    ,0
    ,1
    ,4634
    ,4634
    ,'ONCOUNTER'
    ,now()
    ,700
    ,now()
    ,'System'
FROM ccdb.consumers_new c
JOIN ccdb.bills_new b ON c.consumer_num = b.consumer_number
AND c.cin > (SELECT MAX(cin) FROM ccdb.payments_new);

INSERT INTO ccdb.payment_head_dtls_new
        (
            payment_id
            ,mbc_receipt_id
            ,charge_head_code
            ,amount
            ,tariff_id
            ,creation_dt
            ,created_by
        )
SELECT
        payment_id
        ,receipt_id
        ,'EB'
        ,700
        ,1
        ,now()
        ,'System'
FROM ccdb.payments_new
WHERE payment_id > (SELECT MAX(payment_id) FROM ccdb.payment_head_dtls_new);


END LOOP;

END;

$BODY$
  LANGUAGE plpgsql VOLATILE
 COST 100;
ALTER FUNCTION ccdb.perf_test_new(bigint)
  OWNER TO postgres;

当我在表中没有记录的情况下执行大约 10,000 条记录的函数时,它会在大约 7 分钟内完成,但是当我在初始插入后尝试再运行 10,000 条记录时,它会持续大约 5-6几个小时,但它仍然没有完成,我不得不终止工作。我对 psql 比较陌生,谁能建议我如何提高这种脚本的性能?

谢谢。

【问题讨论】:

***.com/questions/17267417/… @CraigRinger 感谢您的链接,但我觉得这对我没有多大帮助。 您似乎正在尝试进行更新插入。您的实现不是并发安全的,并且不会锁定目标表。如果你想要性能,你应该锁定目标表,然后将你的 upsert 作为一个集合操作,而不是一个(慢得多的)循环。 但是我如何在没有循环的情况下执行此操作。我需要循环,因为我想插入一定数量的记录,这些记录作为参数传递给我运行循环的函数。我觉得需要时间的是,当我检查记录的存在并且当表的大小增加时,所花费的时间将继续增加。我想解决这个问题。 一次检查(比如说)5 条记录通常比检查 1 条记录 5 次要快。至于循环:generate_seriesIN(...) 【参考方案1】:

您可以为循环做的就是使用显式游标并尝试提高性能。

【讨论】:

以上是关于PostgreSQL 提高 PL/pgSQL 函数的性能的主要内容,如果未能解决你的问题,请参考以下文章

有一个PL / pgSQL免费环境可以为PostgreSQL开发吗?

什么 PL/pgSQL 异常与 PostgreSQL 中 Oracle 的 VALUE_ERROR 等效?

有没有为 PostgreSQL 开发的 PL/pgSQL 免费环境?

PL/pgSQL 函数会自行自动提交吗?

PostgreSQL:列名到数组 PL/pgSQL

PostgreSQL-PL/pgSQL控制结构