自定义公式生成伪SQL
程序员文章站
2022-06-11 10:57:23
...
由于小弟在文档编写能力上很不足,所以想通过学习一些别人的东西来提高一下,这个是我接手的一个模块,写出我认识到的文档,其他大家能批评指导。
需求:用户可以*录入公式,并根据公式计算出当前结果。
如公式为 指标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语句。
上一篇: 软件需求说明书的编写提示
下一篇: java动态加载jar