• 利用逆波兰表达式,二叉树对sql语句解析


    做sql的的where子句解析,是出于实际业务很多场景是sql查询,通过sql解析  并穷举各种分支反向生成 测试数据。

    其实有开源的flex和bison来做,但是感觉太重,而且时间成本问题,所以自己来写了个轻量解析方法。

    eg: 有一个where子句如下

    ((handoutstatus = 2 and horrcause = 28 and eventid = 9 ) or (eventid = 8 and handinstatus = 2 and horrcause = 28)) and protocolid = 111

    A)基于上述语句,生成其逆波兰表达式

       首先对运算符分类为(且优先级从高到低):

    | &

    | =, !=,in 。。。

    | and

    | or

    V

    运算符包括: 操作符(=, in 等) 和 连接符(and, or)

    步骤:

    (其中A:操作数栈, B:运算堆栈)

    1. 扫描上面sql语句字符串, 利用空格符获取每一个子串(如 ”((handoutstatus” ).

    2. 对每个子串按照各种情况处理:(function: Infix2Postfix)

    各种情况:(function: In2PostHelper)

    (1) 字段名(子串入A栈)

    (2) 字段的取值 (子串入A栈, 取值可以是固定数值或范围如(1,3, 12) )

    (3) 运算符:(包括and, or)

    具体步骤:

    (a) 若运算符堆栈栈顶的运算符为括号,则直接存入运算符堆栈B。

    (b) 若比运算符堆栈栈顶的运算符优先级高或相等,则直接存入运算符堆栈B。

    (c) 若比运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈B,并将当前运算符压入运算符堆栈(循环此步骤直至不满足条件)。

    (4) 为左括号"(",则直接存入运算符堆栈

    (5) 为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止

    (6) 当以上各种情况都不是时,考虑要进行分解子串:(注:本步骤仅进行一次),将得到的子子串输到算法步骤2.(1)-(5)步骤进行处理(function:    DecStringHelperCplx)。

    考虑子串分解情况:

    (a) case: “and/or(....”,分解得到三个子子串(eg: and, (, 字段名 )输入到2.(1)-(5)步骤进行处理.

    (b) case: in(3,..)))...,子串为包含”in(”: 分解为eg: in, (1,2,12),以及可能含有右括号,得到这些子子串输入到2.(1)-(5)步骤进行处理.

    (c) 含有操作符情况,分析这个子串可能含有一个或两个操作符,如(a&b=0, 含两个:&, =; 或a=0), 含一个=), 以操作符为界限,将左子串,右子串再分解到最小单元(按d步骤来分解)(最小单元:字段名,值,运算符,括号)

    (d) Case: (4,...)))...; (a, ((a, (((...a; 23...)))...(function: DecStringDeepHelper)

    (7) 当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数

    堆栈,直到运算符堆栈为空

    B)  最终对A栈内容(即逆波兰表达式)进行创建二叉树:(function: CreateLinkList)

    Eg: Handoutstatus,2,=,horrcause,28,=,and,eventide,9,=,and,eventid,8,=,handinstatus,2,=,and,horrcause,28,=,and,or,protocolid,111,=,and

    由栈底开始依次取元素建立二叉树, 直至全部处理完

    操作数栈C, 结点栈D

    (1) 如果扫描的项目是操作数(即字段名,值),则将其压入操作数堆栈C,并扫描下

    一个项目。

    (2) 如果扫描的项目是一个&运算符,则对操作数栈的顶上两个操作数执行合并操作, 并入操作数栈C。(eg: A&B<>0)

    (3) 如果扫描的项目是一个二元操作符(=, <>等等),则对操作数栈的顶上两个操作数执行该运算,建立值结点存储,入结点栈D。(eg:Node(protocolid, = , 12), 或Node(1222&umpl, <>, 0),注意对于&符特殊处理,当填充字段值时,要检查是否有&进行特殊处理)

    (4) 若为连接符and, or, 则为它建立一结点,并对结点栈D的顶上两个结点出栈,存储为其左,右子,并将此结点入结点栈D。

    (5) 重复步骤1-5,最后结点堆栈中剩下的最后一个结点指针即指向了二叉树

                          图示:                                                                                

    C)广度优先遍历二叉树,得到一种sql分支(function: OutputFields)

    两个队列A:存字段置正,B:字段置反

    (1) 将根结点入队列A

    (2) 当队列A或B不为空时:

    (a) 从队列B头pop一个结点:

    If 结点域名非and, or

    将各种运算符置反转到=, 将最终值结点存到refMsgConVec

    If 结点域名为and 或or

    让左,右子结点都入B

    (b) 从队列A头pop一个结点:

    If 结点域名非and, or

    // this->ProcessValueNode(pTmpCNode->pCondition, refMsgConVec);

    将各种运算符取正转到=, 将最终值结点存到refMsgConVec

    If结点域名为and

    将左, 右子结点均入队列A,

    If 结点域名为or

    随机让左,右子结点分别入A,B

    Application:

    Infix2Postfix(); //转为逆波兰表达式

    CreateLinkList(); //创建二叉树

    OutputFields(refMsgConVec); //遍历二叉树

  • 相关阅读:
    Mysql优化之6年工作经验总结
    mysql_innodb存储引擎的优化
    十六、MySQL授权命令grant的使用方法
    十五、Mysql字符集的那些事
    十四、索引
    十三、视图
    十二、存储过程
    十一、触发器
    十、存储引擎
    九、备份与恢复
  • 原文地址:https://www.cnblogs.com/europelee/p/3346518.html
Copyright © 2020-2023  润新知