上一篇中,介绍了我们的存储和索引建立过程,这篇将介绍SQL查询、单表查询和TOPN实现。
一、SQL解析
正规的sql解析是用语法分析器,但是我找了好久,只知道可以用YACC、BISON等,sqlite使用的lemon,捣整了一天没实现,就用了python的正则表达式。
1、删除无用的空格、跳格符、换行符等;
我们以分号‘;’作为一个sql语句的结束符,在输入分号之前,我们将输入的sql语句串接成一个string,在将整个sql语句的一些无用的字符删掉,
1 def rmNoUseChar(sql): 2 while sql.find("'") != -1:#将引号删除,不论什么类型都当字符类型处理 3 sql = sql.replace("'","") 4 while sql.find('"') != -1: 5 sql = sql.replace('"','') 6 while sql.find('\t') != -1:#删除制表符 7 sql = sql.replace("\t"," ") 8 while sql.find('\n') != -1:#删除换行符 9 sql = sql.replace("\n"," ")10 statements = sql.split(" ")#分割成列表,删除多余空格后在拼接成字符串11 while "" in statements:12 statements.remove("")13 sql=""14 for stmt in statements:15 sql += stmt+ " "16 return sql[0:-1]#最后一个空格删掉
2、关键词大写;
在sql语句中扫描关键字,将关键字大写。这里我们使用了一个技巧,在每个select语句前面多加一个空格,每个关键字前后都加一个空格,这样可以替换单词的部分,如果不加空格,像charity 就会被替换为CHARrity,这不是我们想要的。
oneKeywords = [" SELECT "," FROM "," WHERE ", " DESC "," ASC ", " DATE "," DAY "," INT "," CHAR "," VARCHAR "," DECIMAL ", " SUM "," AVG ","MAX","MIN"," COUNT "," AS "," TOP "," AND "," OR "] twoKeywords = [" GROUP BY "," ORDER BY "]
3、解析和格式化SELECT子语句;
一个常见的select语句一般包含select、from、where、group by、order by五部分(不考虑嵌套查询),where、group by、order by可以不出现,但如果出现的,在sql语句中必定满足select、from、where、group by、order by的顺序,因此我们定义:
stmtTag = ["SELECT","FROM","WHERE","GROUP BY","ORDER BY",";"]#select 子语句标志词
找到各个子语句的标志词,根据标志词来解析子语句,这里我们定义了一个方法,用来找下一个标志词:
def nextStmtTag(sql,currentTag):#根据当前标志词找下一个标志词 index = sql.find(currentTag,0) for tag in stmtTag: if sql.find(tag,index+len(currentTag)) != -1: return tag
比如我们测试发现sql语句中有WHERE标志词,那么它一定有where子句,我们通过nextStmtTag()方法得到下一个关键词,如果sql中有GROUP BY, 则下一个标志词就是GROUP BY,如果没有GROUP BY而有ORDER BY,那下一个标志词就是ORDER BY,否则下一个标志词就是分号";",因为一个sql中一定有结束符分号。
4、结合元数据表检查语法错误;
解析完sql的子语句后,我们就可以进行简单的语法检查,结合元数据检查WHERE子句的表是否在数据库中存在,以及其他子语句中的属性是否在WHERE子句的表中,检查的过程中,顺便将属性大写,并将的表名添上,属性的格式统一为:[表名].[属性名],同时对多表查询的where条件做优化,即将单表查询条件放在列表的前面,多表连接放在后面。具体请看下面的例子:
我们输入如下sql语句:
select l_orderkey,o_orderdate,o_shippriority, min(l_orderkey) as min_odkey, max(o_shippriority) as max_priority from customer,orders,lineitem where c_mktsegment = "MACHINERY" and c_custkey = o_custkey and l_orderkey = o_orderkey and o_orderdate < "1995-05-20" and l_shipdate > "1995-05-18" group by l_orderkey,o_orderdate,o_shippriority order by o_orderdate desc,o_orderdate;
解析的结果(已通过语法检查):
{'FROM': ['CUSTOMER', 'ORDERS', 'LINEITEM'], 'GROUP': ['LINEITEM.L_ORDERKEY', 'ORDERS.O_ORDERDATE', 'ORDERS.O_SHIPPRIORITY'], 'ORDER': [['ORDERS.O_ORDERDATE', 'DESC'], ['ORDERS.O_ORDERDATE', 'ASC']], 'SELECT': [['LINEITEM.L_ORDERKEY', None, None], ['ORDERS.O_ORDERDATE', None, None], ['ORDERS.O_SHIPPRIORITY', None, None], ['LINEITEM.L_ORDERKEY', 'MIN', 'min_odkey'], ['ORDERS.O_SHIPPRIORITY', 'MAX', 'max_priority']], 'WHERE': [['CUSTOMER.C_MKTSEGMENT', '=', 'MACHINERY'], ['ORDERS.O_ORDERDATE', '<', '1995-05-20'], ['LINEITEM.L_SHIPDATE', '>', '1995-05-18'], ['CUSTOMER.C_CUSTKEY', '=', 'ORDERS.O_CUSTKEY'], ['LINEITEM.L_ORDERKEY', '=', 'ORDERS.O_ORDERKEY']]}
可以看到我们将整个sql解析成一个字典,字典的键是子语句标志词,值是格式化的子语句,这个解析结果跟JSON格式差不多。对于group by和from子语句只是简单的表名和属性名,因此就使用一个list表示,而其他子语句比较复杂,我们对其子语句的每个部分用list表示,如order子语句,不光有属性还有升序或降序描述;而select还有聚集函数和重命名;where子句我们只考虑大于、等于和小于的条件,即每个where条件可以用过一个三元组表示。不出现的部分我们用None补齐。
这就是我们的解析select sql的大体过程,细节不再介绍,因为这个解析方法实在不高明,上不了台面,正规军都是用的句法和语法解析器,我们打游击战的。
查看本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/extra/
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索sql
, select
, 语句
, python文件语句解析
, 空格
, sql语句实现
, sql语句解析
, 子语句查询类型错误
, 一个
, 子查询语句格式
子句
python 实现数据库、简易数据库、简易数据库软件、sql 简易数据库、简易数据库编程软件,以便于您获取更多的相关知识。