欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

自定义公式生成伪SQL

程序员文章站 2022-06-11 11:01:22
...

由于小弟在文档编写能力上很不足,所以想通过学习一些别人的东西来提高一下,这个是我接手的一个模块,写出我认识到的文档,其他大家能批评指导。

需求:用户可以*录入公式,并根据公式计算出当前结果。
如公式为 指标test=#指标1+#指标2+同比增长(#指标3)
每天的batch时会根据不同日期进行计算指标test值,同时可能出现回算的情况。

分析:公式结构包括加减乘除,>=,<=,>,<,IF 判断和一些固定的计算方法(同比增长,环比增长等),所有设计为在公式录入的时间,把公式转化为一个伪SQL的格式,同时在batch计算时替换掉固定位置的字符串,生成一个可以执行的SQL,最终执行的结果就是公式的计算结果。

设计:
指公式用到的符号分为3部分,+,-,*,/,>=,<=,>,<属于操作类符号;IF 判断和一些固定的计算方法(同比增长,环比增长等)属于方法类符号;#指标2等属于自定义符号;
操作类符号:统一定义接口Operator,所有的操作类符号都实现这个接口
Interface Operator {
//操作返回类型
public Type getReturnedType();
//支持的操作类型
public Type[] getSupportedInputType();
//操作符号
public String getWord();
}
方法类符号:统一定义接口Function,所有的方法类符号都实现这个接口
Interface Function {
public Type getReturnedType();
public Type[] getSupportedInputType();
public String getName();
//生成相就的SQL语句
public String toSql();
}
自定义符号:TakeHolder
Interface TakeHolder {
public type getReturnedType();
//自定义的符号的指定字符
public String getStart();
//判断是否有效
public boolean isValid(String token);
//转换
public String translate(String token);
}
定义一个Language用于指定当前模块所支持的操作符号,方法符号,自定义符号;
Interface Language {
public List getSupportedFunctions();
public List getSupportedOperators();
public List getSupportedTakeHolders();
//支持的类型
public List getSupportedTypes();
}
定义一个解析类,存储当前所支持的操作符号,方法符号,自定义符号,并解析用户录入的公式。
class TokenParse {
private HashMap operators = new HashMap();
private HashMap functions = new HashMap();
private HashMap holders = new HashMap();
public TokenParse(Language language) {
}
//解析公式
public List<Token> parse(String formula);
}
当用户输入的公式时,解析每一个字符,判断当前字符属于操作类符号,方法类符号,自定义符号;并存入到指定的token(type, exp)中,

public List<Token> parse(String exp) throws TokenException {
        // asert
        if (exp == null || exp.trim().length() == 0)
            return null;

        List<Token> tokens = new ArrayList<Token>();

        char[] chars = exp.toCharArray();
        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            if (isSeprator(c)) {
                String tmp = sb.toString();
                if (tmp.length() != 0) {
                    Token t = new Token(Token.EXPRESSION, tmp);
                    if (isOperand(tmp)) {
                        t.setType(Token.OPERAND);
                    }
                    tokens.add(t);

                }

                if (isSimpleOperator(c)) {
                    String op = "" + c;
                    Token top = new Token(Token.OPERATOR, op);
                    tokens.add(top);
                }

                sb = new StringBuffer();
                continue;
            }
            if (c == OPENED_BRACKET) {
                int count = 0;
                count++;
                sb.append(c);
                boolean colsed = false;
                for (i = i + 1; i < chars.length; i++) {
                    char jc = chars[i];
                    sb.append(jc);
                    if (jc == OPENED_BRACKET) {
                        count++;

                        continue;
                    }
                    if (c == DOUBLE_QUOTE) {

                        //sb.append(c);
                        boolean qcolsed = false;
                        for (i = i + 1; i < chars.length; i++) {
                            char qc = chars[i];
                            sb.append(qc);

                            if (qc == DOUBLE_QUOTE) {

                                qcolsed = true;

                                break;
                            }

                        }
                        if (!qcolsed)
                            throw new TokenException("双引号不匹配");
                        String tmp = sb.toString();
                        if (tmp.length() != 0) {
                            Token t = new Token(Token.OPERAND, tmp);
                            tokens.add(t);

                        }

                        sb = new StringBuffer();
                        continue;

                    }
                    if (jc == CLOSED_BRACKET) {
                        count--;
                        if (count == 0) {
                            colsed = true;
                            String tmp = sb.toString();
                            String name = tmp.substring(0, tmp
                                    .indexOf(OPENED_BRACKET));
                            if (name.length() == 0) {
                                Token t = new Token(Token.EXPRESSION, tmp);
                                tokens.add(t);

                            } else if (isFunctionName(name)) {
                                Token t = new Token(Token.FUNCTION, tmp);
                                tokens.add(t);

                            } else if (isOperator(name)) {
                                Token t = new Token(Token.OPERATOR, name);
                                tokens.add(t);

                                t = new Token(Token.EXPRESSION, tmp
                                        .substring(tmp.indexOf(OPENED_BRACKET)));
                                tokens.add(t);

                            } else {
                                throw new TokenException("不能识别的函数或操作符:" + name);
                            }
                            sb = new StringBuffer();

                            break;
                        }
                    }

                }
                if (!colsed)
                    throw new TokenException("括号不匹配");
                continue;
            }
           
            if (c == DOUBLE_QUOTE) {

                sb.append(c);
                boolean qcolsed = false;
                for (i = i + 1; i < chars.length; i++) {
                    char qc = chars[i];
                    sb.append(qc);

                    if (qc == DOUBLE_QUOTE) {

                        qcolsed = true;

                        break;
                    }

                }
                if (!qcolsed)
                    throw new TokenException("双引号不匹配");
                String tmp = sb.toString();
                if (tmp.length() != 0) {
                    Token t = new Token(Token.OPERAND, tmp);
                    tokens.add(t);

                }

                sb = new StringBuffer();
                continue;

            }
           
            sb.append(c);

        }
        String tmp = sb.toString();
        if (tmp.length() > 0) {
            Token t = new Token(Token.EXPRESSION, tmp);
            if (isOperand(tmp)) {
                t.setType(Token.OPERAND);
            }
            tokens.add(t);
        }
        if (tokens.size() == 1) {
            Token token = (Token) tokens.get(0);
            if (token.getType() == Token.EXPRESSION) {
                String tmpExp = token.getExp();
                if (tmpExp.charAt(0) == OPENED_BRACKET) {
                    String interal = tmpExp.substring(1, tmpExp.length() - 1);
                    return parse(interal);
                } else {
                    throw new TokenException("不能识别的操作数:" + tmpExp);
                }

            }
            if (token.getType() == Token.OPERATOR) {
                throw new TokenException("表达式尚未结束");
            }
        }
解析List<Token>,递归转化为一个Unit类
Class Unit {
public String operator = null;
public List oprands = new ArrayList();
public Type returnedType;
}
例:指标1+指标2转换的Unit为
Unit.operator = “+”;
Unit.oprands = {[指标1],[指标2]}
Unit.returnedType = numeric;

public Unit compile(List tokens, TakeHolderContainer container)
            throws TokenException {
        Unit u = new Unit();
        Object[] ts = tokens.toArray();
        int length = ts.length;
        if (length == 1) {
            Token t = (Token) ts[0];
            if (t.getType() == Token.OPERATOR) {
                throw new TokenException("表达式没有结束,缺乏操作数");
            }
            if (t.getType() == Token.OPERAND) {
                doOperand(u, t, container);
            }
            if (t.getType() == Token.FUNCTION) {
                doFunction(u, t, container);
            }
            if (t.getType() == Token.EXPRESSION) {
                doExpression(u, t, container);
            }

            return u;
        }
        int top = findTopOperator(ts);
        if (top == -1) {
            throw new TokenException("表达式不完整");
        }
        // "Not" operator
        if (top == 0) {
            Token t = (Token) ts[0];
            u.operator = t.getExp();

            Object[] right = new Object[length - 1];
            System.arraycopy(ts, 1, right, 0, length - 1);
            List rl = Arrays.asList(right);

            Unit ru = this.compile(rl, container);
            String name = t.getExp().toUpperCase();
            Operator op = (Operator) name2Operator.get(name);
            if (op.getSupportedInputTypes()[0].getClass().isAssignableFrom(
                    ru.returnedType.getClass())) {
                u.oprands.add(ru);

                u.ref = op;
                u.returnedType = op.getReturnedType();
                u.type = Unit.OPERATION;
            } else {
                throw new TokenException("t.getExp().toUpperCase()"
                        + "操作符与之后相连的操作数类型不匹配");
            }

        }
        if (top > 0) {
            Token t = (Token) ts[top];
            u.operator = t.getExp();

            Object[] left = new Object[top];
            System.arraycopy(ts, 0, left, 0, top);

            Object[] right = new Object[length - top - 1];
            System.arraycopy(ts, top + 1, right, 0, length - top - 1);

            List ll = Arrays.asList(left);

            Unit lu = this.compile(ll, container);

            List rl = Arrays.asList(right);

            Unit ru = this.compile(rl, container);

            Operator op = (Operator) name2Operator
                    .get(t.getExp().toUpperCase());

            if (op.getSupportedInputTypes()[0].getClass().isAssignableFrom(
                    lu.returnedType.getClass())) {
                u.oprands.add(lu);
            } else {
                throw new TokenException("t.getExp().toUpperCase()"
                        + "操作符与之前相连的操作数类型不匹配");
            }

            if (op.getSupportedInputTypes()[1].getClass().isAssignableFrom(
                    ru.returnedType.getClass())) {
                u.oprands.add(ru);
            } else {
                throw new TokenException("t.getExp().toUpperCase()"
                        + "操作符与之后相连的操作数类型不匹配");
            }
            u.ref = op;
            u.returnedType = op.getReturnedType();
            u.type = Unit.OPERATION;
        }

        return u;
    }
拼装伪SQL
以递归的方式查询Unit,并根据相应的operator产生对应的SQL语句。
相关标签: SQL C C++ C#