KPI考核系统

KPI公式说明

普通公式:公式(100-(R-G)*5)*W,其中R是结果值,G是目标值,W是权重

分段公式:公式>=(R/G,1,1,0.6)*W*100,如上图所示,公式分为三部分。第一部分是‘>=’是标识符,第二部分括号内的‘R/G’,‘1’,‘1’,‘0.6’分别是参数1,参数2,参数3,第三部分是权重(W)和满分分数。其含义是如果R/G>=1则结果为1*W*100,否则结果为0.6*W*100。第二部分中的参数4可以是另一个公式。例如:>=(R/G,1,1,>=(R/G,0.75,0.6,0))*W*100

创建KPI明细考核项

-- auto-generated definition
create table PERFORMANCE_EVALUATION
(
    ID                NUMBER generated as identity,
    KPI_ITME          VARCHAR2(100),
    KPI_INSTRUCTION   VARCHAR2(4000),
    W                 NUMBER,
    G                 NUMBER,
    R                 NUMBER,
    KPI_SCORE_FORMULA CLOB,
    KPI_GROUP         VARCHAR2(50)
)
/

comment on table PERFORMANCE_EVALUATION is '绩效评估项'
/

comment on column PERFORMANCE_EVALUATION.KPI_ITME is '考核项'
/

comment on column PERFORMANCE_EVALUATION.KPI_INSTRUCTION is '指标说明'
/

comment on column PERFORMANCE_EVALUATION.W is '权重'
/

comment on column PERFORMANCE_EVALUATION.G is '目标值'
/

comment on column PERFORMANCE_EVALUATION.R is '结果值'
/

comment on column PERFORMANCE_EVALUATION.KPI_SCORE_FORMULA is '计算公式'
/

comment on column PERFORMANCE_EVALUATION.KPI_GROUP is 'KPI分组'
/

创建计算函数,可以改成触发器形式

分段公式拆解计算

create function apex_cul_spe_kpi(formula varchar)
return number
is
ls_char    varchar(10);
ls_formula varchar(200);
ls_parma_1 varchar(20);
ls_param_2 varchar(20);
ls_param_3 varchar(20);
ls_param_4 varchar(100);
ls_res_1     number(10,2);
ls_res_2     number(10,2);
ls_res_3     number(10,2);
ls_res_4     number(10,2);
begin
    -- 预处理
    select substr(formula, instr(formula, '(') + 1, instr(formula, ')', -1, 1) - instr(formula, '(') - 1)
    into ls_formula
    from dual;

-- 截取判断条件
    select substr(ls_formula, 1, instr(ls_formula, ',') - 1) into ls_parma_1 from dual;
    select substr(ls_formula, instr(ls_formula, ',') + 1, instr(ls_formula, ',', 1, 2) - instr(ls_formula, ',') - 1)
    into ls_param_2
    from dual;
    select substr(ls_formula, instr(ls_formula, ',', 1, 2) + 1,
                  instr(ls_formula, ',', 1, 3) - instr(ls_formula, ',', 1, 2) - 1)
    into ls_param_3
    from dual;
    select substr(ls_formula, instr(ls_formula, ',', 1, 3) + 1) into ls_param_4 from dual;

-- 判断条件计算
    select DBMS_AW.EVAL_NUMBER(ls_parma_1) into ls_res_1 from dual;
    select DBMS_AW.EVAL_NUMBER(ls_param_2) into ls_res_2 from dual;
    select DBMS_AW.EVAL_NUMBER(ls_param_3) into ls_res_3 from dual;

-- 第一个字符判断
    select substr(formula, 1, 1) into ls_char from dual;
    case ls_char
        when '>' then -- 第二个字符判断
        select substr(formula, 2, 1) into ls_char from dual;
        if ls_char = '=' then
-- 计算分段
            if sign(ls_res_1 - ls_res_2) >= 0 then
                return ls_res_3;
            else
                select substr(ls_param_4, 1, 1) into ls_char from dual;
                if ls_char = '>' or ls_char = '<' or ls_char = '=' then
                    return apex_cul_spe_kpi(ls_param_4);
                else
                    select DBMS_AW.EVAL_NUMBER(ls_param_4) into ls_res_4 from dual;
                    return ls_res_4;
                end if;
            end if;
        else
            if sign(ls_res_1 - ls_res_2) > 0 then
                return ls_res_3;
            else
                select substr(ls_param_4, 1, 1) into ls_char from dual;
                if ls_char = '>' or ls_char = '<' or ls_char = '=' then
                    return apex_cul_spe_kpi(ls_param_4);
                else
                    select DBMS_AW.EVAL_NUMBER(ls_param_4) into ls_res_4 from dual;
                    return ls_res_4;
                end if;
            end if;
        end if;
        when '<' then -- 第二个字符判断
        select substr(formula, 2, 1) into ls_char from dual;
        if ls_char = '=' then
-- 计算分段
            if sign(ls_res_1 - ls_res_2) <= 0 then
                return ls_res_3;
            else
                select substr(ls_param_4, 1, 1) into ls_char from dual;
                if ls_char = '>' or ls_char = '<' or ls_char = '=' then
                    return apex_cul_spe_kpi(ls_param_4);
                else
                    select DBMS_AW.EVAL_NUMBER(ls_param_4) into ls_res_4 from dual;
                    return ls_res_4;
                end if;
            end if;
        else
            if sign(ls_res_1 - ls_res_2) < 0 then
                return ls_res_3;
            else
                select substr(ls_param_4, 1, 1) into ls_char from dual;
                if ls_char = '>' or ls_char = '<' or ls_char = '=' then
                    return apex_cul_spe_kpi(ls_param_4);
                else
                    select DBMS_AW.EVAL_NUMBER(ls_param_4) into ls_res_4 from dual;
                    return ls_res_4;
                end if;
            end if;
        end if;
        when '=' then -- 第二个字符判断
        select substr(formula, 2, 1) into ls_char from dual;
        if ls_char = '>' then
-- 计算分段
            if sign(ls_res_1 - ls_res_2) >= 0 then
                return ls_res_3;
            else
                select substr(ls_param_4, 1, 1) into ls_char from dual;
                if ls_char = '>' or ls_char = '<' or ls_char = '=' then
                    return apex_cul_spe_kpi(ls_param_4);
                else
                    select DBMS_AW.EVAL_NUMBER(ls_param_4) into ls_res_4 from dual;
                    return ls_res_4;
                end if;
            end if;
        else
            if sign(ls_res_1 - ls_res_2) <= 0 then
                return ls_res_3;
            else
                select substr(ls_param_4, 1, 1) into ls_char from dual;
                if ls_char = '>' or ls_char = '<' or ls_char = '=' then
                    return apex_cul_spe_kpi(ls_param_4);
                else
                    select DBMS_AW.EVAL_NUMBER(ls_param_4) into ls_res_4 from dual;
                    return ls_res_4;
                end if;
            end if;
        end if;
        else return 0;
        end case;
end;
/

主计算函数,如果是普通公式直接计算,如果是嵌套执行apex_cul_spe_kpi

create FUNCTION apex_calculate_score(v_formula_a IN VARCHAR2, w IN NUMBER, r IN NUMBER, g IN NUMBER)
    RETURN varchar2 IS
    V_FORMULA varchar(100);
    V_TEMP    varchar(100);
    V_SCORE   number(10);
    V_WEIGHT  number(10, 2);
    V_GOAL    number(10,2);
    V_CHAR    varchar(1);
    V_PARAM   number(20, 2);
begin
    -- 获取参数
    V_FORMULA := v_formula_a;
    V_GOAL := g;
    V_WEIGHT := w;

    -- 公式参数替换
    select replace(V_FORMULA, 'W', V_WEIGHT) into V_FORMULA from dual;
    select replace(V_FORMULA, 'G', V_GOAL) into V_FORMULA from dual;
    select replace(V_FORMULA, 'R', r) into V_FORMULA from dual;

    select substr(V_FORMULA, 1, 1) into V_CHAR from dual;
    if V_CHAR = '>' or V_CHAR = '<' or V_CHAR = '=' then
        select substr(V_FORMULA, 1, instr(V_FORMULA, ')',-1)) into V_TEMP from dual;
        V_PARAM := APEX_CUL_SPE_KPI(V_TEMP);
        select substr(V_FORMULA, instr(V_FORMULA, ')',-1)+1) into V_FORMULA from dual;
        V_FORMULA := TO_CHAR(V_PARAM) || V_FORMULA;
    end if;
--     return V_PARAM;
    -- 解析公式并计算
    select dbms_aw.eval_number(V_FORMULA) into V_SCORE from dual;
    if V_SCORE < 0 then
        V_SCORE := 0;
    end if;
    if V_SCORE > 100*V_WEIGHT then
        V_SCORE := 100*V_WEIGHT;
    end if;
    RETURN V_SCORE;
end;
/

执行运算

SELECT APEX_CALCULATE_SCORE('>=(R,G,W*100,W*100-(G-R))',0.2,80,100) FROM DUAL;