计算项预加工的实质逻辑存储过程。

1 调用格式

p_dn_ci_computer(
 v_app              VARCHAR (50),
 v_ci               VARCHAR (50), 
 v_cf               VARCHAR (50),
 OUT i_count_field  INT(2),
 OUT v_err          VARCHAR(4096),
 OUT v_log          TEXT
)
参数说明:

v_app:字符串类型;乐创者的应用名。因为1个乐创者服务支持多个应用,所以需要指定1个明确的应用。

v_ci:字符串类型;计算项名称。

v_cf:字符串类型;字段名称。

i_count_field:整型;返回计算的字段数量。

v_err:字符串类型;返回错误报告信息。

v_log:字符串类型;返回日志信息。

2 实现原理

本存储过程由 p_dn_ci 进行调用,完成lczWord进行计算项预加工的实质逻辑。

处理过程大致是:

1)启动事务;

2)清除历史数据;

3)构造参数组合信息;

3)按字段轮询,每个字段基于参数组合表进行轮询;

4)如果计算依据是一个SQL,则执行SQL;

5)如果计算依据是一个用户自定义存储过程,则判断是在 p_dn_ci_computer 来实现参数轮询呢还是用户自定义存储过程中实现参数轮询。

6)如果不需要由 p_dn_ci_computer 来进行参数轮询,则直接执行用户自定义存储过程即可,在 p_dn_ci_computer 中调用用户的自定义存储过程。

7)如果需要由 p_dn_ci_computer 来进行参数轮询,则需要轮询参数,每种参数组合分别执行用户自定义存储过程。

8)如执行顺利则递交事务,否则事务回滚。

3 存储过程

存储过程如下:

CREATE DEFINER=`root`@`%` PROCEDURE `p_dn_ci_compute`(v_App VARCHAR (50),-- 应用
    v_CI VARCHAR (50),                         -- 计算项
    v_CF VARCHAR (50),                         -- 字段
    OUT i_Count_Field INT(2),            -- 返回计算的字段数量
    OUT v_Err VARCHAR(4096),            -- 返回错误报告
    OUT v_Log TEXT)
LABEL1:BEGIN
    /* -------------------------------------------
        1、定义全局变量
    ------------------------------------------- */
    DECLARE v_t1_fields VARCHAR(200) DEFAULT '';
    DECLARE v_t1_create VARCHAR(500) DEFAULT '';

    DECLARE    v_where_result VARCHAR(500) DEFAULT '';
    DECLARE    v_str_result VARCHAR(500) DEFAULT '';

    DECLARE    v_from VARCHAR(1024) DEFAULT '';
    DECLARE i_debug TINYINT(1) DEFAULT 1;

    SET v_Log = '';
    SET v_Err = '';

    START TRANSACTION;

    IF i_debug = 1 THEN
        SET v_Log = CONCAT(v_Log,'{\"step\":\"开始事务\"}');
    END IF;

    /* -------------------------------------------
        1、FOR 清空已有结果数据
    ------------------------------------------- */
    BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:清空结果数据失败';
        -- FIX 修正删除数据错误问题,改为cszh_guid,修正删除单个字段时删除所有字段值的异常
        IF v_CF = '' THEN
            DELETE FROM dn_cc_jsx_zd_jg WHERE cszh_guid IN (SELECT GUID FROM dn_cc_jsx_cs_zh WHERE YYMC = v_App AND JSXMC = v_CI);
        ELSE
            DELETE FROM dn_cc_jsx_zd_jg WHERE zdmc = v_CF AND cszh_guid IN (SELECT GUID FROM dn_cc_jsx_cs_zh WHERE YYMC = v_App AND JSXMC = v_CI);
        END IF;

        IF i_debug = 1 THEN
            SET v_Log = CONCAT(v_Log,',{\"step\":\"清空了现有结果数据\"}');
        END IF;
    END;

    -- 错误检查
    BEGIN
        IF v_Err <> '' THEN
            ROLLBACK;
            LEAVE LABEL1;
        END IF;
    END;

    /* -------------------------------------------
        2、FOR 参数组合
    ------------------------------------------- */

    -- 2.1 准备创建临时表 t1,构造v_t1_create和v_t1_fields
    BEGIN
        DECLARE i_found INT(2) DEFAULT 0;
        DECLARE    v_para_name VARCHAR(50) DEFAULT '';

        DECLARE cur_paras CURSOR FOR SELECT CSMC FROM dn_dy_jsx_cs WHERE YYMC = v_App AND JSXMC = v_CI ORDER BY CSSX;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET i_found = NULL;
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:无法打开dn_dy_jsx_cs表';

        OPEN cur_paras;

        FETCH cur_paras INTO v_para_name;
        IF i_found IS NULL THEN
            SET v_Err = 'Err:dn_dy_jsx_cs表没有检索到数据';
            CLOSE cur_paras;
            ROLLBACK;
            LEAVE LABEL1;
        END IF;

        WHILE (i_found IS NOT NULL) DO
            IF v_t1_create = '' THEN
                SET v_t1_create = v_para_name;
                SET v_t1_fields = v_para_name;
            ELSE
                SET v_t1_create = CONCAT(v_t1_create,',',v_para_name);
                SET v_t1_fields = CONCAT(v_t1_fields,',',v_para_name);
            END IF;

            SET v_t1_create = CONCAT(v_t1_create,' VARCHAR(50)');

            FETCH cur_paras INTO v_para_name;
        END WHILE;
        CLOSE cur_paras;
    END;

    -- 错误检查
    BEGIN
        IF v_Err <> '' THEN
            ROLLBACK;
            LEAVE LABEL1;
        END IF;
    END;

    -- 2.2 创建临时表t1
    BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:无法创建临时表t1';

        SET @t1_create = CONCAT('CREATE TEMPORARY TABLE t1(',v_t1_create,');');

        IF i_debug = 1 THEN
            SET v_Log = CONCAT(v_Log,',{\"step\":\"创建临时表t1,有几个全局参数就会有几个字段,sql:',replace(@t1_create,"\"","'"),'\"}');
        END IF;

        PREPARE pr1 FROM @t1_create;
        EXECUTE pr1;
        DROP PREPARE pr1;
    END;

    -- 错误检查
    BEGIN
        IF v_Err <> '' THEN
            ROLLBACK;
            LEAVE LABEL1;
        END IF;
    END;

    -- 2.3 生成记录准备插入临时表t1
    BEGIN
        -- 1) 构造Cross联合表
        DECLARE i_found    INT(2) DEFAULT 0;
        DECLARE    v_para_name VARCHAR(50) DEFAULT '';
        DECLARE v_sql VARCHAR(1024) DEFAULT '';
        DECLARE v_default_value VARCHAR(500) DEFAULT '';
        DECLARE v_id_code VARCHAR(50);
        DECLARE v_branch_selected TINYINT(1);                                -- 是否选择枝干节点:1 - 是,0 - 不是
        DECLARE v_leaf_field VARCHAR(50);                                        -- 叶子节点字段名称

        DECLARE cur_cross CURSOR FOR
            SELECT 
                dn_dy_qjcs.CSMC, dn_dy_qjcs.CSMRZ, dn_dy_qjcs.ZDSQL,dn_dy_qjcs.BMZD, dn_dy_qjcs.sfxzzgjd, dn_dy_qjcs.yzjdzd
            FROM
                dn_dy_qjcs 
            INNER JOIN 
                dn_dy_jsx_cs
            ON 
                dn_dy_qjcs.CSMC = dn_dy_jsx_cs.CSMC AND dn_dy_qjcs.YYMC = dn_dy_jsx_cs.YYMC
            WHERE 
                dn_dy_jsx_cs.YYMC = v_App AND dn_dy_jsx_cs.JSXMC = v_Ci
            ORDER BY dn_dy_jsx_cs.CSSX;

        DECLARE    CONTINUE HANDLER FOR NOT FOUND SET i_found = NULL;
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:无法打开交叉查询';

        OPEN cur_cross;

        IF i_debug = 1 THEN
            SET v_Log = CONCAT(v_Log,',{\"step\":\"打开交叉查询成功\"}');
        END IF;

        FETCH cur_cross INTO v_para_name,v_default_value,v_sql,v_id_code,v_branch_selected,v_leaf_field;
        IF i_found IS NULL THEN
            SET v_Err = 'Err:交叉查询没有检索到数据';
            CLOSE cur_cross;
            DROP TEMPORARY TABLE IF EXISTS t1;
            ROLLBACK;
            LEAVE LABEL1;
        END IF;

        WHILE (i_found IS NOT NULL) DO
            -- FIX 修正只有默认值的情况
            IF v_sql IS NULL AND v_default_value IS NOT NULL THEN
                SET v_sql = CONCAT('SELECT \'',v_default_value,'\' res_value');
                SET v_id_code = 'res_value';
            END IF;
            IF v_branch_selected = 0 AND v_leaf_field IS NOT NULL AND v_leaf_field != '' THEN
                SET v_sql = CONCAT(
                                            'SELECT\r\n', 
                                                v_id_code,
                                            '\r\nFROM', '\r\n(' , 
                                                v_sql, ') inner_' , v_id_code,
                                            '\r\nWHERE\r\n',
                                                v_leaf_field, ' = \'1\''
                                        );
            END IF;

            IF v_from = '' THEN
                SET v_from = CONCAT(
                    '(\r\nSELECT ',
                    v_id_code,
                    ' FROM\r\n(',
                    v_sql,
                    ') inner_',v_id_code,'\r\n) ',
                    v_para_name
                );

                SET v_where_result = CONCAT(
                    'CONCAT("',
                    v_para_name,
                    '=",'
                    '"\'",',v_para_name,',"\'"'
                );

                SET v_str_result = CONCAT(
                    'CONCAT(\r\n"',
                    v_para_name,
                    '=",\r\n',
                    v_para_name,',\r\n',
                    '";"'
                );
            ELSE
                SET v_from = CONCAT(
                    v_from,
                    '\r\n,\r\n(\r\nSELECT ',
                    v_id_code,
                    ' FROM\r\n(',
                    v_sql,
                    ') inner_',
                    v_para_name,
                    '\r\n) ',
                    v_para_name
                );
                SET v_where_result = CONCAT(
                    v_where_result,
                    '," AND ",',
                    '"',
                    v_para_name,
                    '=",'
                    '"\'",',v_para_name,',"\'"'
                );
                SET v_str_result = CONCAT(
                    v_str_result,
                    ',\r\n',
                    '"',
                    v_para_name,
                    '=",\r\n',
                    v_para_name,',\r\n',
                    '";"'
                );
            END IF;

            FETCH cur_cross INTO v_para_name,v_default_value,v_sql,v_id_code,v_branch_selected,v_leaf_field;
        END WHILE;
        CLOSE cur_cross;
    END;

    -- 错误检查
    BEGIN
        IF v_Err <> '' THEN
            DROP TEMPORARY TABLE IF EXISTS t1;
            ROLLBACK;
            LEAVE LABEL1;
        END IF;
    END;

    -- 2.4 插入数据至t1临时表
    BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:把交叉查询结果插入到临时表t1失败';

        SET v_where_result = CONCAT(v_where_result,'\r\n)');
        SET v_str_result = CONCAT(v_str_result,'\r\n)');

        SET @t1_into = CONCAT(
                'INSERT INTO t1 (',
                v_t1_fields,
                ')\r\n SELECT * FROM \r\n',
                v_from
        );

        PREPARE pr1_1 FROM @t1_into;
        EXECUTE pr1_1;
        DROP PREPARE pr1_1;

        IF i_debug = 1 THEN
            SET v_Log = CONCAT(v_Log,',{\"step\":\"t1表的作用是进行多个参数组合。插入t1表成功,sql:',replace(@t1_into,"\"","'"),'\"}');
        END IF;

    END;

    -- 错误检查
    BEGIN
        IF v_Err <> '' THEN
            SET v_Err = CONCAT(v_Err,'\r\nsql is:\r\n',@t1_into);
            DROP TEMPORARY TABLE IF EXISTS t1;
            ROLLBACK;
            LEAVE LABEL1;
        END IF;
    END;

    -- 2.5 创建和初始化临时表t2
    BEGIN
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:创建并设置临时表t2内容失败';

        CREATE TEMPORARY TABLE t2 (for_where VARCHAR(1024),for_Str VARCHAR(1024));
        SET @t2_create = CONCAT('INSERT INTO t2(for_where,for_str)\r\nSELECT\r\n',v_where_result,' for_where,\r\n',v_str_result,' for_str\r\nFROM t1');
        PREPARE pr2 FROM @t2_create;
        EXECUTE pr2;
        DROP PREPARE pr2;

        DROP TEMPORARY TABLE IF EXISTS t1;

        IF i_debug = 1 THEN
            SET v_Log = CONCAT(v_Log,
                ',{\"step\":\"t2表的作用是存储多个参数组合对应的sql where条件和组合串。创建临时表t2并插入数据成功,sql:',replace(@t2_create,"\"","'"),'\"}');
        END IF;
    END;

    -- 错误检查
    BEGIN
        IF v_Err <> '' THEN
            DROP TEMPORARY TABLE IF EXISTS t2;
            DROP TEMPORARY TABLE IF EXISTS t1;
            ROLLBACK;
            LEAVE LABEL1;
        END IF;
    END;

    -- ---------------------------------------
    -- 3、根据t2进行轮询,维护参数组合表
    -- ---------------------------------------
    BEGIN
        DECLARE i_count_para INT(2) DEFAULT 0;
        DECLARE i_found TINYINT(1) DEFAULT 0;
        DECLARE i_index INT(2);

        DECLARE cur_t2 CURSOR FOR SELECT * FROM t2;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET i_found = NULL;
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:打开临时表t2失败';

        OPEN cur_t2;

        IF i_debug = 1 THEN
            SET v_Log = CONCAT(v_Log,',{\"step\":\"打开t2表成功,开始针对t2的记录进行轮询,轮询次数代表参数的组合数量(日志仅记录第1个参数组合)...\"}');
        END IF;

        FETCH cur_t2 INTO v_where_result,v_str_result;
        IF i_found IS NULL THEN
            SET v_Err = 'p_dn_ci_compute Err:open t2 is succeed,but not data!';
            CLOSE cur_t2;
            DROP TEMPORARY TABLE IF EXISTS t2;
            ROLLBACK;
            LEAVE LABEL1;
        END IF;

        WHILE i_found IS NOT NULL DO
            SET i_count_para = i_count_para+1;

            -- 3.4.1 插入不存在的参数组合
            SELECT COUNT(*) INTO i_index FROM dn_cc_jsx_cs_zh WHERE YYMC = v_App AND JSXMC = v_CI AND CSZHNR = v_str_result;
            -- PROBLEM 参数组合只增不减会不会造成参数组合表的爆炸呢?
            IF i_index = 0 THEN    
                INSERT INTO dn_cc_jsx_cs_zh(GUID,YYMC,JSXMC,CSZHNR,CSZHTJ) VALUES (UUID(),v_App,v_Ci,v_str_result,v_where_result);
            END IF;

            FETCH cur_t2 INTO v_where_result,v_str_result;
        END WHILE ;

        CLOSE cur_t2;
        DROP TEMPORARY TABLE IF EXISTS t2;

        IF i_debug = 1 THEN
            SET v_Log = CONCAT(v_Log,',{\"step\":\"t2表轮询成功结束,共检查和维护了',i_count_para,'个参数组合\"}');
        END IF;

    END;

    -- 错误检查
    BEGIN
        IF v_Err <> '' THEN
            DROP TEMPORARY TABLE IF EXISTS t2;
            ROLLBACK;
            LEAVE LABEL1;
        END IF;
    END;
    -- ---------------------------------------
    -- 4、按字段轮询,每个字段基于参数组合表进行轮询
    -- ---------------------------------------
    BEGIN
        DECLARE i_found_field TINYINT(1) DEFAULT 0;
        DECLARE v_field_name VARCHAR(50) DEFAULT '';
        DECLARE v_execute_type TINYINT(1) DEFAULT 0;
        DECLARE v_procedure_type TINYINT(1) DEFAULT 0;
        DECLARE v_sql VARCHAR(1024);

        DECLARE cur_field Cursor FOR 
            SELECT ZDMC,ZXLX,JSSQL,LXLX FROM dn_dy_jsx_zd 
            WHERE YYMC = v_App AND JSXMC = v_CI AND ( v_CF <> '' AND ZDMC = v_CF OR v_CF = '' ); 
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET i_found_field= NULL;
        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:打开字段表失败';

        IF i_debug = 1 THEN
            SET v_Log = CONCAT(v_Log,',{\"step\":\"开始字段轮询计算...\"}');
        END IF;

        OPEN cur_field;

        FETCH cur_field INTO v_field_name,v_execute_type,v_sql,v_procedure_type;
        IF i_found_field IS NULL THEN
            SET v_Err = '严重错误:检索不到计算项的字段';
            CLOSE cur_field;
            ROLLBACK;
            LEAVE LABEL1;
        END IF;

        SET i_Count_Field = 0;

        WHILE i_found_field IS NOT NULL DO

                IF v_sql = '' OR v_sql IS NULL THEN
                    SET v_Err = CONCAT('严重错误:找不到',v_CI,'.',v_field_name,'对应的SQL或存储过程定义信息');
                    CLOSE cur_field;
                    ROLLBACK;
                    LEAVE LABEL1;
                END IF;

                SET i_Count_Field = i_Count_Field + 1;

                IF v_execute_type = 2 THEN 
                -- -----------------------------
                -- 如果计算形式是一个存储过程
                -- -----------------------------

                    IF v_procedure_type = 1 THEN
                    -- ----------------------------------
                    -- 如果需要由本文件进行轮询参数组合
                    -- ----------------------------------
                        BEGIN
                            DECLARE v_guid VARCHAR(50) DEFAULT '';
                            DECLARE v_where VARCHAR(500) DEFAULT '';
                            DECLARE v_group VARCHAR(500) DEFAULT '';
                            DECLARE i_found_para TINYINT(1) DEFAULT 0;
                            DECLARE v_ok INT(1) DEFAULT 0;
                            DECLARE cur_para Cursor FOR 
                                SELECT GUID,CSZHTJ,CSZHNR FROM dn_cc_jsx_cs_zh 
                                WHERE YYMC = v_App AND JSXMC = v_CI; 
                            DECLARE CONTINUE HANDLER FOR NOT FOUND SET i_found_para = NULL;
                            DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:打开参数组合表失败';

                            SET @sum_sql = CONCAT(
                                    'call ',
                                    v_sql,'(',
                                    '"',v_App,'",',
                                    '"',v_CI,'",',
                                    '"',v_field_name,'",?,?)'
                                );

                            IF i_debug = 1 THEN
                                SET v_Log = CONCAT(v_Log,',{\"step\":\"尝试为 [',v_CI,'.',v_field_name,'] 进行参数组合轮询并逐个启动存储过程:',replace(@sum_sql,"\"","'"),'\"}');
                            END IF;

                            OPEN cur_para;

                            FETCH cur_para INTO v_guid,v_where,v_group;
                            WHILE i_found_para IS NOT NULL DO

                                SET v_ok = DATANEW_FIND_UNUSE_P_COMBINE(v_group,v_App,v_CI);
                                IF v_ok = 1 THEN
                                    SET @v_guid = v_guid;
                                    SET @v_where = v_where;

                                    PREPARE pr FROM @sum_sql;
                                    EXECUTE pr USING @v_guid,@v_where;

                                    drop PREPARE pr;
                                END IF;

                                FETCH cur_para INTO v_guid,v_where,v_group;

                            END WHILE;
                            CLOSE cur_para;
                        END;

                    ELSE
                        -- ----------------------------------
                        -- 如果由该存储过程自行进行轮询参数组合
                        -- ----------------------------------

                        SET @sum_sql = CONCAT(
                                'call ',
                                v_sql,'(',
                                '"',v_App,'",',
                                '"',v_CI,'",',
                                '"',v_field_name,'"',
                                ')'
                            );

                        IF i_debug = 1 THEN
                            SET v_Log = CONCAT(v_Log,',{\"step\":\"',i_Count_Field,' 为 [',v_CI,'.',v_field_name,'] 启动自身具备参数轮询能力的存储过程:',replace(@sum_sql,"\"","'"),'\"}');
                        END IF;

                        PREPARE pr FROM @sum_sql;
                        EXECUTE pr;

                        DROP PREPARE pr;
                    END IF;

                ELSE
                -- -----------------------------
                -- 如果计算形式是一个SQL
                -- -----------------------------
                    BEGIN
                        DECLARE v_guid VARCHAR(50) DEFAULT '';
                        DECLARE v_where VARCHAR(500) DEFAULT '';
                        DECLARE v_group VARCHAR(500) DEFAULT '';
                        DECLARE v_ok INT(1) DEFAULT 0;
                        DECLARE i_found_para TINYINT(1) DEFAULT 0;
                        DECLARE cur_para Cursor FOR 
                            SELECT GUID,CSZHTJ,CSZHNR FROM dn_cc_jsx_cs_zh 
                            WHERE YYMC = v_App AND JSXMC = v_CI; 
                        DECLARE CONTINUE HANDLER FOR NOT FOUND SET i_found_para = NULL;
                        DECLARE EXIT HANDLER FOR SQLEXCEPTION SET v_Err = '严重错误:打开参数组合表或插入结果值失败';

                        OPEN cur_para;

                        FETCH cur_para INTO v_guid,v_where,v_group;
                        WHILE i_found_para IS NOT NULL DO
                            SET v_ok = DATANEW_FIND_UNUSE_P_COMBINE(v_group,v_App,v_CI);
                            IF v_ok = 1 THEN
                                SET @sum_sql = 
                                    CONCAT(
                                        'INSERT INTO dn_cc_jsx_zd_jg(GUID,\r\nCSZH_GUID,\r\nZDMC,\r\nZDJG)\r\nVALUES(\r\n',
                                        '"',UUID(),'"',
                                        ',\r\n',
                                        '"',v_guid,'",\r\n',
                                        '"',v_field_name,'"',
                                        ',\r\n',
                                        '(\r\nSELECT SUM(',v_field_name,') FROM\r\n',
                                        '(\r\n',v_sql,') sub1',
                                        '\r\nWHERE\r\n',v_where,
                                        '\r\n)\r\n)'
                                    );

                                PREPARE pr FROM @sum_sql;
                                EXECUTE pr;
                                DROP PREPARE pr;
                            END IF;
                            FETCH cur_para INTO v_guid,v_where,v_group;
                        END WHILE;
                        CLOSE cur_para;
                    END;

                END IF;
            FETCH cur_field INTO v_field_name,v_execute_type,v_sql,v_procedure_type;
        END WHILE;
        CLOSE cur_field;

    END;

    -- 错误检查
    BEGIN
        IF v_Err <> '' THEN

            -- 发生异常时,记录 SQL
            IF i_debug = 1 THEN
                SET v_Log = CONCAT(v_Log,',{\"step\":\"尝试为',v_CI,'.',v_field_name,'启动SQL:',replace(@sum_sql,"\"","'"),'\"}');
            END IF;

            SET v_Err = CONCAT(v_Err,'\r\ndetaile sql is:\r\n',@sum_sql);

            ROLLBACK;
            LEAVE LABEL1;
        END IF;
    END;

    COMMIT;

    IF i_debug = 1 THEN
        SET v_Log = CONCAT(v_Log,',{\"step\":\"成功递交事务\"}');
    END IF;

END
作者:敏  创建时间:2024-09-14 15:42
最后编辑:敏  更新时间:2025-03-07 10:05