优化 PL/SQL 代码 [关闭]

Posted

技术标签:

【中文标题】优化 PL/SQL 代码 [关闭]【英文标题】:Optimize PL/SQL code [closed] 【发布时间】:2014-10-08 09:39:10 【问题描述】:

我知道在没有规范的情况下很难对代码进行一些优化,但我也没有规范。任何人都可以在下面的代码中进行一些优化吗?因为这段代码执行超过 6 天,但我们需要在 6 或 8 小时内运行它。有什么建议吗?

PROCEDURE kzt_2_27_p ( p_start_date DATE, p_end_date DATE )
AS
    CURSOR cur1
        IS
            SELECT cal.id,
                 cal.dt,
                 cal.id_period,
                 per.period_name
            FROM xxkkbi_dim_calendar cal,
                 xxkkbi_dim_period per
            WHERE 
                ( cal.dt BETWEEN p_start_date AND p_end_date ) 
            AND 
                per.id = cal.id_period
            ORDER BY cal.dt;

    row1            cur1%ROWTYPE;
    l_year_sum      NUMBER;
    l_id_specific   NUMBER;
    l_region        VARCHAR2 ( 10 CHAR );
    l_period_name   VARCHAR2 ( 10 CHAR );
    l_id_region     NUMBER;
    l_id_date       NUMBER;
    l_id_period     NUMBER;
    l_segment4      VARCHAR2 ( 30 CHAR );
    --------- For Log -------------------------------
    l_fct           VARCHAR2 ( 20 ) := 'FCT_2_27';
    l_date          DATE;
    l_descr         VARCHAR2 ( 2000 CHAR ) := '';
    l_err_code      VARCHAR2 ( 20 CHAR );
    l_err_msg       VARCHAR2 ( 2000 CHAR );
--------- End For Log ----------------------------
BEGIN
    pkg_fct_log.fct_log_2 ( p_fct_name  => l_fct, p_oper_name => 'START PROCEDURE' );

    FOR row1 IN cur1
    LOOP
        l_date := row1.dt;
        l_period_name := row1.period_name;
        l_id_period := row1.id_period;
        l_id_date := row1.id;

        DELETE FROM xxkkbi.fct_2_27 WHERE id_date = l_id_date AND id_period = l_id_period;

        FOR row2 IN ( SELECT id, code
                      FROM xxkkbi_dim_region
                      WHERE code NOT IN ('0000', '0077', '5899', '0098', '0099', 'T', '6300') AND id > -1 )
        LOOP
            l_region := row2.code;
            l_id_region := row2.id;

            INSERT INTO temp_2_27
                    SELECT cc.segment4,
                            SUM ( DECODE ( cc.segment3, '000000000', NVL ( accounted_cr, 0 ) - NVL ( accounted_dr, 0 ), 0 ) ) sum_in,
                            SUM ( DECODE ( cc.segment3, '019999999', NVL ( accounted_cr, 0 ) - NVL ( accounted_dr, 0 ), 0 ) ) sum_out,
                            SUM ( DECODE ( cc.segment3, '000000000', 0, '019999999', 0, NVL ( accounted_cr, 0 ) - NVL ( accounted_dr, 0 ) ) ) sum_zach,
                            id_specific
                    FROM gl_gl_je_lines l,
                         gl_gl_je_headers h,
                        (SELECT 
                            code_combination_id,
                            specific.id id_specific,
                            segment1,
                            segment2,
                            segment3,
                            segment4,
                            segment7
                        FROM  
                            apps_gl_code_combinations cc,
                            xxkkbi_dim_specific specific
                        WHERE 
                            chart_of_accounts_id = 50408
                        AND   
                            template_id IS NULL
                        AND ( 
                            segment1 = CASE
                            WHEN 
                                SUBSTR ( l_region, 3, 2 ) = '01'
                            THEN 
                                '02'
                            ELSE
                                '03'
                            END )
                        AND 
                            segment2 LIKE CASE WHEN 
                                SUBSTR ( l_region, 3, 2 ) = '01'
                            THEN
                                SUBSTR ( l_region, 1, 2 ) || '%'
                            ELSE
                                l_region
                            END
                        AND ( segment3 IN ('000000000', '019999999') OR segment3 BETWEEN '021999999' AND '023999999' )
                        AND segment4 BETWEEN '100000' AND '8ßßßßß'
                        AND segment5 = '0'
                        AND segment6 = '0000000'
                        AND segment7 = '1'
                        AND segment4 = specific.code
                        AND specific.date_end >= l_date
                        ) cc
                    WHERE h.ledger_id = 2021
                        AND h.actual_flag = 'A'
                        AND h.currency_code = 'KZT'
                        AND h.je_header_id = l.je_header_id
                        AND l.code_combination_id = cc.code_combination_id
                        AND l.ledger_id = h.ledger_id
                        AND l.status = 'P'
                        AND l.period_name = l_period_name
                        AND l.effective_date = l_date
                    GROUP BY 
                        cc.segment4,
                        cc.id_specific;

            FOR row3 IN ( 
                SELECT 
                    segment4,
                    sum_in,
                    sum_out,
                    sum_zach,
                    id_specific
                    FROM temp_2_27 )
            LOOP
                l_segment4 := row3.segment4;
                l_id_specific := row3.id_specific;

                SELECT NVL ( SUM ( sum_spec ), 0 )
                INTO l_year_sum
                FROM (
                    SELECT 
                        cc.segment4,
                        -1 * end_of_date_balance_num sum_spec
                    FROM apps_gl_daily_balances_v db,
                        (SELECT 
                            code_combination_id,
                            segment1,
                            segment2,
                            segment3,
                            segment4,
                            segment7
                        FROM 
                            apps_gl_code_combinations cc,
                            xxkkbi_dim_specific specific2
                        WHERE 
                            chart_of_accounts_id = 50408
                        AND template_id IS NULL
                        AND ( segment1 = CASE
                            WHEN 
                                SUBSTR ( l_region, 3, 2 ) = '01'
                            THEN
                                '02'
                            ELSE
                                '03'
                            END )
                        AND segment2 LIKE CASE
                            WHEN 
                                SUBSTR ( l_region, 3, 2 ) = '01'
                            THEN
                                SUBSTR ( l_region, 1, 2 ) || '%'
                            ELSE
                                l_region
                            END
                        AND segment3 IN ('000000000', '019999999')
                        AND segment4 = l_segment4
                        AND segment5 = '0'
                        AND segment6 = '0000000'
                        AND segment7 = '1'
                        AND segment4 = specific2.code
                        AND specific2.date_end >= l_date
                        ) cc
                    WHERE   
                        db.code_combination_id = cc.code_combination_id
                        AND db.ledger_id = 2021
                        AND db.actual_flag = 'A'
                        AND db.currency_type = 'U'
                        AND db.currency_code = 'KZT'
                        AND db.period_set_name = 'КЗ Календарь'
                        AND db.period_name = l_period_name
                        AND db.accounting_date = l_date
                    UNION ALL 
                        SELECT 
                            cc.segment4,
                            NVL ( accounted_cr, 0 ) - NVL ( accounted_dr, 0 ) sum_spec
                        FROM 
                            gl_gl_je_lines l,
                            gl_gl_je_headers h,
                            (SELECT 
                                code_combination_id,
                                segment1,
                                segment2,
                                segment3,
                                segment4,
                                segment7
                            FROM 
                                apps_gl_code_combinations cc,
                                xxkkbi_dim_specific specific2
                            WHERE 
                                chart_of_accounts_id = 50408
                            AND 
                                template_id IS NULL
                            AND ( segment1 = CASE
                                WHEN 
                                    SUBSTR ( l_region, 3, 2 ) = '01'
                                THEN
                                    '02'
                                ELSE
                                    '03'
                                END )
                            AND segment2 LIKE CASE 
                                WHEN 
                                    SUBSTR ( l_region, 3, 2 ) = '01'
                                THEN
                                    SUBSTR ( l_region, 1, 2 ) || '%'
                                ELSE
                                    l_region
                                END
                            AND segment3 BETWEEN '021999999' AND '023999999'
                            AND segment4 = l_segment4
                            AND segment5 = '0'
                            AND segment6 = '0000000'
                            AND segment7 = '1'
                            AND segment4 = specific2.code
                            AND specific2.date_end >= l_date
                            ) cc
                        WHERE 
                            h.ledger_id = 2021
                            AND h.actual_flag = 'A'
                            AND h.currency_code = 'KZT'
                            AND h.je_header_id = l.je_header_id
                            AND l.code_combination_id = cc.code_combination_id
                            AND l.ledger_id = h.ledger_id
                            AND l.status = 'P'
                            AND l.period_name = l_period_name
                            AND l.effective_date BETWEEN TRUNC ( l_date, 'MM' ) 
                            AND l_date
                        UNION ALL 
                            SELECT 
                                cc.segment4,
                                NVL ( begin_balance_cr, 0 ) - NVL ( begin_balance_dr, 0 ) sum_spec
                            FROM 
                                gl_gl_balances db,
                                (SELECT 
                                    code_combination_id,
                                    segment1,
                                    segment2,
                                    segment3,
                                    segment4,
                                    segment7
                                FROM 
                                    apps_gl_code_combinations cc,
                                    xxkkbi_dim_specific specific2
                                WHERE 
                                    chart_of_accounts_id = 50408
                                AND template_id IS NULL
                                AND ( segment1 = CASE 
                                    WHEN 
                                        SUBSTR ( l_region, 3, 2 ) = '01'
                                    THEN
                                        '02'
                                    ELSE
                                        '03'
                                    END )
                                AND segment2 LIKE CASE 
                                    WHEN 
                                        SUBSTR ( l_region, 3, 2 ) = '01'
                                    THEN
                                        SUBSTR ( l_region, 1, 2 ) || '%'
                                    ELSE
                                        l_region
                                    END
                                AND segment3 BETWEEN '021999999' AND '023999999'
                                AND segment4 = l_segment4
                                AND segment5 = '0'
                                AND segment6 = '0000000'
                                AND segment7 = '1'
                                AND segment4 = specific2.code
                                AND specific2.date_end >= l_date
                                ) cc
                            WHERE 
                                db.code_combination_id = cc.code_combination_id
                                AND db.ledger_id = 2021
                                AND db.actual_flag = 'A'
                                AND db.currency_code = 'KZT'
                                AND db.period_name = l_period_name
                );

                pkg_fct_log.fct_log_2 (
                    p_fct_name  => l_fct,
                    p_oper_name => 'START INSERT',
                    p_rows_date => l_date,
                    p_oper_descr => l_descr
                    || ' l_id_specific='
                    || l_id_specific
                    || ' l_year_sum='
                    || l_year_sum 
                );

                INSERT INTO 
                    xxkkbi.fct_2_27
                SELECT 
                    l_id_date,
                    l_id_period,
                    -1,
                    l_id_specific,
                    l_id_region,
                    row3.sum_in,
                    row3.sum_out,
                    row3.sum_zach,
                    l_year_sum
                FROM DUAL;

                pkg_fct_log.fct_log_2 (
                    p_fct_name  => l_fct,
                    p_oper_name => 'END INSERT',
                    p_rows_count => 1,
                    p_rows_date => l_date,
                    p_oper_descr => l_descr
                    || ' l_id_specific='
                    || l_id_specific
                    || ' l_year_sum='
                    || l_year_sum 
                );
            END LOOP;

            pkg_fct_log.fct_log_2 ( 
                p_fct_name  => l_fct,
                p_oper_name => 'START INSERT',
                p_rows_date => l_date,
                p_oper_descr => l_descr 
            );

            INSERT INTO 
                xxkkbi.fct_2_27
            SELECT 
                l_id_date AS id_date,
                l_id_period,
                -1,
                id_specific,
                l_id_region,
                0,
                0,
                0,
                sum_spec
            FROM 
            (  
                SELECT 
                    t1.id_specific,
                    NVL ( SUM ( t1.sum_spec ), 0 ) sum_spec
                    FROM 
                        (SELECT 
                            id_specific,
                            -1 * end_of_date_balance_num sum_spec
                            FROM 
                                apps_gl_daily_balances_v db,
                                (SELECT 
                                    specific2.id id_specific,
                                    code_combination_id,
                                    segment1,
                                    segment2,
                                    segment3,
                                    segment4,
                                    segment7
                                    FROM 
                                        apps_gl_code_combinations cc,
                                        xxkkbi_dim_specific specific2
                                        WHERE 
                                            chart_of_accounts_id = 50408
                                            AND template_id IS NULL
                                            AND ( segment1 = CASE 
                                                WHEN 
                                                    SUBSTR ( l_region, 3, 2 ) = '01'
                                                THEN
                                                    '02'
                                                ELSE
                                                    '03'
                                                END )
                                            AND segment2 LIKE CASE
                                                WHEN 
                                                    SUBSTR ( l_region, 3, 2 ) = '01'
                                                THEN
                                                    SUBSTR ( l_region, 1, 2 ) || '%'
                                                ELSE
                                                    l_region
                                                END
                                            AND segment3 IN ('000000000', '019999999')
                                            AND segment5 = '0'
                                            AND segment6 = '0000000'
                                            AND segment7 = '1'
                                            AND segment4 = specific2.code
                                            AND specific2.date_end >= l_date
                                ) cc
                            WHERE 
                                db.code_combination_id = cc.code_combination_id
                            AND db.ledger_id = 2021
                            AND db.actual_flag = 'A'
                            AND db.currency_type = 'U'
                            AND db.currency_code = 'KZT'
                            AND db.period_set_name = 'ÊÇ Êàëåíäàðü'
                            AND db.period_name = l_period_name
                            AND db.accounting_date = l_date
                            UNION ALL
                                SELECT 
                                    id_specific,
                                    NVL ( accounted_cr, 0 ) - NVL ( accounted_dr, 0 ) sum_spec
                                FROM 
                                    gl_gl_je_lines l,
                                    gl_gl_je_headers h,
                                    (SELECT 
                                        specific2.id id_specific,
                                        code_combination_id,
                                        segment1,
                                        segment2,
                                        segment3,
                                        segment4,
                                        segment7
                                    FROM 
                                        apps_gl_code_combinations cc,
                                        xxkkbi_dim_specific specific2
                                    WHERE 
                                        chart_of_accounts_id = 50408
                                    AND template_id IS NULL
                                    AND ( segment1 = CASE
                                        WHEN 
                                            SUBSTR ( l_region, 3, 2 ) = '01'
                                        THEN
                                            '02'
                                        ELSE
                                            '03'
                                        END )
                                    AND segment2 LIKE CASE
                                        WHEN 
                                            SUBSTR ( l_region, 3, 2 ) = '01'
                                        THEN
                                            SUBSTR ( l_region, 1, 2 ) || '%'
                                        ELSE
                                            l_region
                                        END
                                    AND segment3 BETWEEN '021999999' AND '023999999'
                                    AND segment5 = '0'
                                    AND segment6 = '0000000'
                                    AND segment7 = '1'
                                    AND segment4 = specific2.code
                                    AND specific2.date_end >= l_date
                                    ) cc
                                WHERE 
                                    h.ledger_id = 2021
                                AND h.actual_flag = 'A'
                                AND h.currency_code = 'KZT'
                                AND h.je_header_id = l.je_header_id
                                AND l.code_combination_id = cc.code_combination_id
                                AND l.ledger_id = h.ledger_id
                                AND l.status = 'P'
                                AND l.period_name = l_period_name
                                AND l.effective_date BETWEEN TRUNC ( l_date, 'MM' )
                                AND l_date
                                UNION ALL 
                                    SELECT 
                                        id_specific,
                                        NVL ( begin_balance_cr, 0 ) - NVL ( begin_balance_dr, 0 ) sum_spec
                                    FROM 
                                        gl_gl_balances db,
                                        (SELECT 
                                            specific2.id id_specific,
                                            code_combination_id,
                                            segment1,
                                            segment2,
                                            segment3,
                                            segment4,
                                            segment7
                                        FROM 
                                            apps_gl_code_combinations cc,
                                            xxkkbi_dim_specific specific2
                                        WHERE 
                                            chart_of_accounts_id = 50408
                                        AND template_id IS NULL
                                        AND ( segment1 = CASE
                                            WHEN 
                                                SUBSTR ( l_region, 3, 2 ) = '01'
                                            THEN
                                                '02'
                                            ELSE
                                                '03'
                                            END )
                                        AND segment2 LIKE CASE
                                            WHEN 
                                                SUBSTR ( l_region, 3, 2 ) = '01'
                                            THEN
                                                SUBSTR ( l_region, 1, 2 ) || '%'
                                            ELSE
                                                l_region
                                            END
                                        AND segment3 BETWEEN '021999999' AND '023999999'
                                        AND segment5 = '0'
                                        AND segment6 = '0000000'
                                        AND segment7 = '1'
                                        AND segment4 = specific2.code
                                        AND specific2.date_end >= l_date
                                        ) cc
                                    WHERE 
                                        db.code_combination_id = cc.code_combination_id
                                        AND db.ledger_id = 2021
                                        AND db.actual_flag = 'A'
                                        AND db.currency_code = 'KZT'
                                        AND db.period_name = l_period_name
                                        AND segment4 NOT IN (SELECT segment4 FROM temp_2_27)
                        ) t1
                    GROUP BY t1.id_specific
            );

            pkg_fct_log.fct_log_2 ( 
                p_fct_name  => l_fct,
                p_oper_name => 'END INSERT',
                p_rows_count => SQL%ROWCOUNT,
                p_rows_date => l_date,
                p_oper_descr => l_descr 
            );
            COMMIT;

        END LOOP;

    END LOOP;

    pkg_fct_log.fct_log_2 ( 
        p_fct_name  => l_fct,
        p_oper_name => 'END PROCEDURE' 
    );
    RETURN;
EXCEPTION
    WHEN 
        OTHERS
    THEN
        BEGIN
            l_err_code := SQLCODE;
            l_err_msg  := SUBSTR ( SQLERRM, 1, 2000 );
            pkg_fct_log.fct_log_2 ( 
                p_fct_name  => l_fct,
                p_oper_name => 'EXCEPTION',
                p_err_code  => l_err_code,
                p_err_msg   => l_err_msg 
            );
        END;
END kzt_2_27_p;

【问题讨论】:

六天?涉及多少行,您的日志记录是否显示活动和时间?你做了什么来调查它在哪里以及为什么很慢?你确定有什么东西一直在阻止它吗?所有嵌套循环及其插入和其中的子查询都可能被消除并替换为每个表的单个插入,但我怀疑你会发现有人愿意尝试为你重写整个事情。 @AlexPoole,当我刷新收集所有数据的表时,1 分钟内大约有 1 个新行。可以清楚的看到,有很多同类型的嵌套查询。 那么您需要查看各个语句 - 我猜子查询正在杀死每个插入,并且可能会被连接替换。但是您需要摆脱循环并批量执行操作。分组思考,不要从程序(逐行)方法开始。 并且开发人员已经开始(ab)使用CASE 像任何东西一样。 @LalitKumarB - 是的 - 带有易读性复合体的臭铁杆 - 他们应该像 REAL MEN 一样滥用 DECODE !!!!! 【参考方案1】:

    将操作拆分为不同的操作,而不是 将它们全部保存在一个过程中。

    ROW-by-ROWSLOW-by-SLOW,利用BULK处理和 摆脱FOR LOOP

    调优是一个手动过程,PL/SQL 和 SQL 是不同的引擎。两个引擎之间的每个上下文切换都会产生开销。如果 PL/SQL 代码循环通过一个集合,对集合中的每个项目执行相同的 DML 操作,则可以在一个操作中将整个集合减少 context switches bulk binding 为 DML 语句。

    使用FORALL 语法,它允许我们将集合的内容绑定到单个DML 语句,允许为集合中的每一行运行DML,而无需每次都进行上下文切换。

如果可能,我希望将每个 INSERT 都作为单独的 SQL。只有您知道环境规范和限制。

【讨论】:

以上是关于优化 PL/SQL 代码 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何优化使用游标的 PL/SQL 代码

Oracle PL/SQL 代码约定 [关闭]

我想学习 PL/SQL [关闭]

PL/SQL 中的异常处理。 [关闭]

使用PL/SQL developer概览图剖析pl/sql代码

如何打印出非质数? PL/SQL [关闭]