`
icedcoco
  • 浏览: 38586 次
社区版块
存档分类
最新评论

自定义公式生成伪SQL

阅读更多

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

需求:用户可以自由录入公式,并根据公式计算出当前结果。
如公式为 指标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语句。
分享到:
评论

相关推荐

    excel生成sql脚本

    "Excel生成SQL脚本"这个主题涉及到如何利用Excel的功能来自动化创建SQL语句,这对于数据库管理员和数据分析师来说是一个非常实用的技巧。 首先,我们需要理解表结构说明。在“表结构说明及SQL生成文档.xls”中,...

    java 公式解析源码

    它允许用户自定义公式,使得程序能够动态计算基于特定输入的数据。在Java开发中,这样的库通常用于财务软件、数据分析应用或者任何需要进行复杂计算的系统。 首先,我们要了解公式解析的基本概念。公式解析是将字符...

    触发器和用户自定义函数

    ### 触发器和用户自定义函数:深入解析 #### 触发器(Trigger) 触发器是数据库系统中一种特殊类型的存储过程,它被设计来响应特定的数据库操作,如`INSERT`、`UPDATE`、`DELETE`。当这些操作在特定的表上执行时,...

    sqlserver 根据数据表生成excel数据字典

    在SQL Server中,生成数据字典是数据库管理和分析的重要步骤,它可以帮助我们了解数据库的结构、字段信息以及数据类型等。通常,数据字典是以文档形式存在的,方便开发者和DBA进行查阅。在这个场景中,我们将探讨...

    经典SQL脚本大全

    │ │ 2.5.3 工作日处理函数(自定义节假日).sql │ │ 2.5.4 计算工作时间的函数.sql │ │ │ └─其他 │ 交叉表.sql │ 任意两个时间之间的星期几的次数-横.sql │ 任意两个时间之间的星期几的次数-纵.sql │ ...

    PowerBuilder中采用动态SQL实现公式处理.pdf

    动态SQL语句以字符串形式存在于程序中,在运行时生成并发送到数据库服务器执行。这种方式允许在程序执行时检查SQL语法的正确性,因此需要特别注意避免语法错误。 在公式处理方面,主要分为公式校验和公式运算两步:...

    使用sql server 系统表简化您的操作

    这些系统表可以帮助我们更高效地管理数据库中的对象,例如查看库中所有用户表、找出库中所有用户表带有自定义公式的列、删除库中用户表的所有数据产生的脚本以及删除库中用户表的产生脚本等。 #### 1. 查看库中所有...

    自定义报表开发,自定义报表工具

    4. **计算与公式**:支持自定义计算公式和表达式,允许用户在报表中进行复杂的计算,如求和、平均值、增长率等。 5. **交互性**:自定义报表工具应具有良好的交互性,允许用户通过点击、拖拽等方式与报表进行互动,...

    Sqlserver2000经典脚本

    │ 2.5.3 工作日处理函数(自定义节假日).sql │ │ 2.5.4 计算工作时间的函数.sql │ │ │ └─其他 │ 交叉表.sql │ 任意两个时间之间的星期几的次数-横.sql │ 任意两个时间之间的...

    表达式变量批量替换器(SQL批量替换)

    这款工具允许用户自定义变量的位置,并能从外部文本文件导入替换数据,从而实现SQL语句的快速批量生成。这一功能在处理大量相似SQL语句时,极大地提高了工作效率。 在SQL编程中,变量常常被用于存储和传递数据。...

    在水晶报表中使用SQL数据库

    同时,水晶报表支持创建自定义公式,用于数据转换、计算或逻辑判断。 6. **分组和汇总**:水晶报表提供了强大的分组和汇总功能。可以基于一个或多个字段进行分组,并对每个组进行总计、平均、最大值、最小值等计算...

    快速生成SQL临时表、合并列的高效率辅助Excel

    该工具利用Excel快速生成SQL Server和MySQL的临时表、多行快速合 1、支持SQL Server和MySQL的with语句以及临时表 2、支持自定义至多3列 3、支持多行,行上限为Excel的最大行数。(只需要把结果生成列第三行的公式...

    通过groovy自定义函数实现提取明细表字段至主表字段.rar

    这样的操作对于数据同步、报表生成以及业务流程自动化等场景非常常见。 Groovy是一种动态、灵活的Java平台语言,它具有简洁的语法和强大的元编程能力。在“致远”这样的企业级应用系统中,Groovy常被用来编写自定义...

    SQL-excel-database-sql

    2. **报表生成**:从SQL数据库提取数据,用Excel构建动态报表,VBA可帮助更新报表并自定义格式。 3. **数据分析**:通过VBA将SQL查询结果导入Excel,然后利用Excel的分析工具(如数据透视表、图表等)进行深入分析。...

    DEMO自定义报表工具

    它允许用户不仅可以选择展示的数据列,还能调整排序方式、过滤条件,甚至自定义计算公式,以满足各种业务场景的需求。这种工具通常包含以下几个关键模块: 1. 数据源管理:连接到不同的数据库或者API接口,获取所需...

    ASP.NET数据库可视化自定义控件表单源码

    表格斜线示例,交叉表,行间列间计算公式,多种输入方式,固定列显示,表格多选示例,两个表格之间互选,多层表头示例,联动选择数据,联动选择SQL数据)3:树控件(可多选的树,由SQL语句生成树,可编辑的树控件,...

    自定义报表程序源代码

    用户可以根据需求选择数据源、设置查询条件、调整报表布局和样式,甚至添加计算公式,生成满足业务需求的报表。 2. 数据获取与处理: 报表程序首先需要连接到数据源,如数据库、API接口或文件。源代码可能包含SQL...

    EXCEL 常用SQL语句解释+VBA 完全手册

    Excel中的SQL(Structured Query Language)允许用户对数据进行高效检索、管理与分析,而VBA(Visual Basic for Applications)则是一种编程语言,用于自定义Excel的功能和自动化任务。 SQL在Excel中的应用主要体现...

    Delphi自定义报表

    1. **可视化设计**:提供类似Word的界面,用户可以直接在界面上绘制报表结构,调整字段位置,添加计算公式等。 2. **数据绑定**:支持多种数据库连接,如ADO、BDE等,可以直接绑定到数据库表或查询,实时更新报表...

    U8自定义报表功能介绍

    - **标签公式**:允许用户自定义计算公式,进一步提升报表的计算能力。 5. **权限控制**与**二次开发接口**:U8自定义报表还提供了权限控制机制,确保数据安全。此外,通过COM接口和WEB应用支持,方便进行二次开发...

Global site tag (gtag.js) - Google Analytics