高大上技术之sql解析

Question: 为何sql解析和高大上有关系?
Answer:因为数据库永远都是系统的核心,CRUD如此深入码农的内心。。。如果能把CRUD改造成高大上技术,如此不是造福嘛。。。

CRUD就是Create, Read, Update, Delete,转换成sql语句就是insert, select, update, delete

普通场景下,insert也就是一个insert了,没什么高深。。。
高并发场景下,insert就不是一个insert了,而是千千万万个insert。。。可以用到的技术有排队、分表、分区、分仓、缓存同步

普通场景下,select也就是一个select了,没什么高深。。。
高并发场景下,select就不是一个select了,而是千千万万,再千千万万个select。。。可以用到的技术有缓存、普通读写分离、深入读写分离、不锁、past锁、还有分表、分区、分仓。。。

你说这么多东西,是全部在一个sql中全部自动化掉好呢,还是让我们码农一个一个考虑,再一个一个写成代码逻辑的好?

肯定两种声音都有,还肯定有第三种声音。。。所以我还是照着我自己的思路来说吧,你们随便发挥想象。。。

我要让一个sql全部解决上面的效果,或者接近上面的效果

如何解决,那就是,以SELECT语句为例

  1. 解析SELECT语句
  2. 解析牵涉到的表、字段、主键
  3. 解析是否用到了自己扩展的dsl函数
  4. 找到相应表的分区函数
  5. 找到相应表的缓存配置
  6. 找到dsl函数对应的真实函数
  7. 其他

比如有2个SELECT语句:

  1. SELECT UserID, UserName, Age FROM Users WHERE UserID=‘某个guid‘
  2. SELECT COUNT(1) FROM Users

很简单的两句sql,可是Users是个虚拟表,真实表有16个表:Users.[A-F], Users.[0-9],分表策略为根据主键ID的第一个字母来分表, 因此:

  • 第一句sql需要先解析where条件中UserID=‘guid‘这个UserID是否为pkid,以及这个‘guid‘的值,然后根据guid的值调用分表策略函数得到相应的分表后缀,然后用类似下面这个sql来真实查询:SELECT UserID, UserName, Age FROM [Users.A] WHERE UserID=‘axxxxx-xxxxx-xxxx-xx‘
  • 第二句sql其实是最终变成了16条sql来得到各个分表的count值,然后在程序中累加这些分表的count值

其他:

  • 其他类似缓存、队列、自定义的扩展函数,都类似于上可以得到解决。

由于只是个demo,所以没有实现上述全部功能,我们只说下关键原理、和代码。。。

我们用antlr来做词法解析、语法解析,然后再用tree walker把antlr解析出来的东西转换为我们要的数据结构,比如:SelectTerms, TableName, WhereClause, OrderByClause等

奥,我们还得写一个规则文件让Antlr吃进去,然后antlr就能调用tree walker生成我们要的数据结构了

(大家赶紧补下编译原理之类的基础知识以及ANTLR知识)

grammar SelectSQL;

/*
 * Parser Rules
 */

compileUnit
    :    start
    ;

/*
 * Lexer Rules
 */

WS
    :    [ \t\n\r]+ -> skip
    ;

COMMA:‘,‘;
SELECT: ‘SELECT‘;
STAR:‘*‘;
FROM:‘FROM‘;
WHERE:‘WHERE‘;
ORDERBY:‘ORDER BY‘;
DIRECTION:‘ASC‘|‘DESC‘;
CHAR: ‘a‘..‘z‘|‘A‘..‘Z‘;
NUM: ‘0‘..‘9‘;
STRING:‘\‘‘ .*? ‘\‘‘;
LB:‘(‘;
RB:‘)‘;
LBRACE:‘[‘;
RBRACE:‘]‘;
CONDITIONS_OPERATOR
    :‘AND‘
    |‘OR‘
    ;
CONDITION_OPERATOR
    :‘=‘
    |‘>‘
    |‘<‘
    |‘<>‘
    |‘!=‘
    |‘>=‘
    |‘<=‘
    ;
FCOUNT:‘COUNT‘;

start
    :statement_list
    ;

statement_list
    :statement statement*
    ;

statement
    :selectStatement
    ;

selectStatement
    :selectStmt fromStmt whereStmt? orderbyStmt?
    ;

selectStmt
    :SELECT columns
    ;

columns
    :column (COMMA column)*
    ;

column
    : identifier
    | LBRACE identifier RBRACE
    | functionStmt
    | STAR
    ;

functionStmt
    :function LB (parameters) RB
    ;

function
    :FCOUNT
    ;

parameters
    : parameter (COMMA parameter)*
    ;

parameter
    : identifier
    | integer
    | string
    | STAR
    ;

fromStmt
    :FROM table
    ;

table
    : identifier
    | LBRACE identifier RBRACE
    ;

whereStmt
    : WHERE conditions
    ;

conditions
    : condition (CONDITIONS_OPERATOR condition)*
    ;

condition
    :left CONDITION_OPERATOR right
    ;

left
    : parameter
    ;

right
    : parameter
    ;

orderbyStmt
    :ORDERBY sortStmt
    ;

sortStmt
    : sortCondition (COMMA sortCondition)*
    ;

sortCondition
    :sortColumn DIRECTION
    ;

sortColumn
    : identifier
    | LBRACE identifier RBRACE
    ;

identifier
    :CHAR (CHAR|NUM)*
    ;
integer
    :NUM+
    ;
string
    : STRING
    ;

真心呼唤广大开发人员深入编译原理之类的基础技术!

在eclipse中输入解析sql文本后,会被解析成tree

开源世界真强大啊,有yacc, flex, bison, antlr这些现成的解析工具。

我们先在eclipse中把规则测试通过后,再把这个.g4规则文件拷贝到我们的visual studio中,如下:

然后只要这个g4文件一保存,antlr的vs插件就会自动根据规则文件生成相关名称的词法解析类、文法解析类、以及我们即将要改写的TreeListener

SelectSQLBaseListener:就是antlr插件自动生成的抽象类,我们的改动都是基于这个类,来做override改写(针对规则的enter/exit)

EnterXXXXX/ExitXXXX: 对应规则文件中的规则名称,Enter/Exit代表进入规则以及离开规则之前的行为动作

demo控制台程序运行输出效果:

输入SQL:

                    SELECT * FROM users
                    SELECT userId, userName FROM users
                    SELECT COUNT(1) FROM users
                    SELECT COUNT(*) FROM users
                    SELECT userId, userName FROM users ORDER BY userName DESC
                    SELECT userId, userName FROM users WHERE userId=‘1212121‘ ORDER BY userName DESC

输出SQL:
          select * from [users.0]
          select * from [users.1]
          select * from [users.2]
          select * from [users.3]
          select * from [users.4]
          select * from [users.5]
          select * from [users.6]
          select * from [users.7]
          select * from [users.8]
          select * from [users.9]
          select * from [users.a]
          select * from [users.b]
          select * from [users.c]
          select * from [users.d]
          select * from [users.e]
          select * from [users.f]
          select userId, userName from [users.0]
          select userId, userName from [users.1]
          select userId, userName from [users.2]
          select userId, userName from [users.3]
          select userId, userName from [users.4]
          select userId, userName from [users.5]
          select userId, userName from [users.6]
          select userId, userName from [users.7]
          select userId, userName from [users.8]
          select userId, userName from [users.9]
          select userId, userName from [users.a]
          select userId, userName from [users.b]
          select userId, userName from [users.c]
          select userId, userName from [users.d]
          select userId, userName from [users.e]
          select userId, userName from [users.f]
          select COUNT(1) from [users.0]
          select COUNT(1) from [users.1]
          select COUNT(1) from [users.2]
          select COUNT(1) from [users.3]
          select COUNT(1) from [users.4]
          select COUNT(1) from [users.5]
          select COUNT(1) from [users.6]
          select COUNT(1) from [users.7]
          select COUNT(1) from [users.8]
          select COUNT(1) from [users.9]
          select COUNT(1) from [users.a]
          select COUNT(1) from [users.b]
          select COUNT(1) from [users.c]
          select COUNT(1) from [users.d]
          select COUNT(1) from [users.e]
          select COUNT(1) from [users.f]
          select COUNT(*) from [users.0]
          select COUNT(*) from [users.1]
          select COUNT(*) from [users.2]
          select COUNT(*) from [users.3]
          select COUNT(*) from [users.4]
          select COUNT(*) from [users.5]
          select COUNT(*) from [users.6]
          select COUNT(*) from [users.7]
          select COUNT(*) from [users.8]
          select COUNT(*) from [users.9]
          select COUNT(*) from [users.a]
          select COUNT(*) from [users.b]
          select COUNT(*) from [users.c]
          select COUNT(*) from [users.d]
          select COUNT(*) from [users.e]
          select COUNT(*) from [users.f]
          select userId, userName from [users.0]  order by userName DESC
          select userId, userName from [users.1]  order by userName DESC
          select userId, userName from [users.2]  order by userName DESC
          select userId, userName from [users.3]  order by userName DESC
          select userId, userName from [users.4]  order by userName DESC
          select userId, userName from [users.5]  order by userName DESC
          select userId, userName from [users.6]  order by userName DESC
          select userId, userName from [users.7]  order by userName DESC
          select userId, userName from [users.8]  order by userName DESC
          select userId, userName from [users.9]  order by userName DESC
          select userId, userName from [users.a]  order by userName DESC
          select userId, userName from [users.b]  order by userName DESC
          select userId, userName from [users.c]  order by userName DESC
          select userId, userName from [users.d]  order by userName DESC
          select userId, userName from [users.e]  order by userName DESC
          select userId, userName from [users.f]  order by userName DESC
          select userId, userName from [users.0] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.1] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.2] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.3] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.4] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.5] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.6] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.7] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.8] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.9] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.a] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.b] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.c] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.d] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.e] WHERE userId=‘1212121‘ order by userName DESC
          select userId, userName from [users.f] WHERE userId=‘1212121‘ order by userName DESC

希望大家能对基础技术真正感兴趣,赶紧学习编译原理、antlr吧。

很抱歉没能提供详细原理说明,大家baidubaidu就都有了。

代码下载 http://files.cnblogs.com/files/aarond/SQLParser_Select.rar

时间: 2024-10-13 01:46:20

高大上技术之sql解析的相关文章

MySQL架构总览-&gt;查询执行流程-&gt;SQL解析顺序

前言: 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了. 本文将从MySQL总体架构--->查询执行流程--->语句执行顺序来探讨一下其中的知识. 一.MySQL架构总览: 架构最好看图,再配上必要的说明文字. 下图根据参考书籍中一图为原本,再在其上添加上了自己的理解. 从上图中我们可以看到,整个架构分为两层,上层是MySQLD的被称为的'SQL Layer',下层是各种各样对上提供接口的存储引擎,被称为'Storage Engin

步步深入:MySQL架构总览-&gt;查询执行流程-&gt;SQL解析顺序

前言: 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了. 本文将从MySQL总体架构--->查询执行流程--->语句执行顺序来探讨一下其中的知识. 一.MySQL架构总览: 架构最好看图,再配上必要的说明文字. 下图根据参考书籍中一图为原本,再在其上添加上了自己的理解. 从上图中我们可以看到,整个架构分为两层,上层是MySQLD的被称为的‘SQL Layer’,下层是各种各样对上提供接口的存储引擎,被称为‘Storage Engin

JAVA - Sql解析工具fdb-sql-parser简单使用

由于想要解决Mybatis分页插件中count查询效率问题,因为order by很影响效率,所以需要一种方式处理sql,将order by 语句去掉. 试了好几个sql解析工具,最后选择了fdb-sql-parser. Maven依赖: <dependency> <groupId>com.foundationdb</groupId> <artifactId>fdb-sql-parser</artifactId> <version>1.

XML涉及到的相关技术(编写+解析)

xml:即可扩展标记语言,用于描述关系型数据,也经常用作软件的配置文件:     1,编写xml文档一般基于一个约束文档,该文档用于规定xml的书写规范,常用的约束技术有        (1)XML DTD(早期技术) (2)XML Schema(现在主流技术)     2,编写好xml文档后,就需要对该文档进行解析,常见解析方式有两种:        (1)DOM解析(SUN官方标准)(2)sax解析(实际应用标准)     常用的解析开发包:Jaxp(SUN)(最差),Jdom,dom4j(

基于简单sql语句的sql解析原理及在大数据中的应用

李万鸿 通常sql语法解析都是以lex.yacc进行分析为基础的,是逐个字符进行分析,性能不高,如果基于没有子查询的sql语句进行解析,则速度会提高许多,在此对其原理加以说明. 一般sql语句十分复杂,包含多层嵌套,但其中有规律可循,其特点是所有的语句基本模式是一样的,即:都是SELECT  FROM  WHERE类型的简单语句,因此,可以把嵌套的语句逐条解析为这种简单语句.由于sql语句的语法是一样的,所以可以用递归的方法对语句加以解析,从而把sql语句解析为多条简单的子SQL语句.然后通过一

自己实现一个SQL解析引擎

自己实现一个SQL解析引擎 功能:将用户输入的SQL语句序列转换为一个可执行的操作序列,并返回查询的结果集. SQL的解析引擎包括查询编译与查询优化和查询的运行,主要包括3个步骤: 查询分析: 制定逻辑查询计划(优化相关) 制定物理查询计划(优化相关) 查询分析: 将SQL语句表示成某种有用的语法树. 制定逻辑查询计划: 把语法树转换成一个关系代数表达式或者类似的结构,这个结构通常称作逻辑计划. 制定物理查询计划:把逻辑计划转换成物理查询计划,要求指定操作执行的顺序,每一步使用的算法,操作之间的

sql 解析字符串添加到临时表中 sql存储过程in 参数输入

sql 解析字符串添加到临时表中  sql存储过程in 参数输入 解决方法 把字符串解析 添加到 临时表中 SELECT * into #临时表   FROM dbo.Func_SplitOneColumnTabel('001,gf', ',') select * from 表 where ID in (select id from 临时表) Create function [dbo].[Func_SplitOneColumnTabel] (@str nvarchar(max),@split v

数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 解析(六)之删除SQL

关注微信公众号:[芋道源码]有福利: RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复.甚至不知道如何读源码也可以请教噢. 新的源码解析文章实时收到通知.每周更新一篇左右. 认真的源码交流微信群. 本文主要基于 Sharding-JDBC 1.5.0 正式版 1. 概述 2. DeleteStatement 3. #p

数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(二)之SQL解析

关注微信公众号:[芋艿的后端小屋]有福利: RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址 您对于源码的疑问每条留言都将得到认真回复.甚至不知道如何读源码也可以请教噢. 新的源码解析文章实时收到通知.每周更新一篇左右. 认真的源码交流微信群. 1. 概述 2. SQLParsingEngine 3. SQLParser SQL解析器 3.2.1 #parse