SQL解析及体会

最近在搞一个项目需要对实时流数据进行数据关联,直接查询数据库处理效率太低,直接访问redis也不太理想,一个数据要进行10次左右查询,每秒多则几百条数据,就要进行几千次查询;若是实时程序挂了,还需要进行批处理,一旦批处理,每秒要处理的数据就更多了,为了提升效率,就采用内存缓存+redis的模式,因为这些数据是用户访问网页的数据,数据具有连贯性,上一次的查询结果短时间内还用得到,所以内存缓存可以极大地减少从redis查询的次数。
最开始想到的是根据输入的字段,先到内存中查询数据,查不到然后到redis查询,查回来后放到内存中进行缓存。数据关联的逻辑是在代码中写死的,比如有[a,b,c]字段,需要从多个数据源ds_a,ds_b,ds_c中关联出[a1,b1,c1]字段。代码大致如下:

a1 = ds_a.get(a as id) .get(colname of a1);
b1 = ds_b.get(b as id).get(colname of b1);
c1 = ds_c.get(c as id).get(colname of c1);

写代码的时候,相关的数据都封装成了具有固定属性的对象,但是写完之后麻烦就来了,此前没做过类似用户行为分析的东西,客户的统计需求变化很快,每新增一个统计,或者变换一个统计口径,我们都需要修改代码,然后编译,重新部署,代价很大;于是就萌生了将数据的关联逻辑通过配置来实现的想法,关联逻辑能实现动态更新,这样就不用成天改代码了。我们想了好久,希望数据的关联逻辑能够像SQL那样精炼易懂,同时也足够灵活。

说了半天才进入正题O(∩_∩)O~,请原谅用了这么大篇幅去交代背景。因为我写这些是为了记住自己这回的想法。

既然想通过类似SQL的表达方式来描述逻辑,那么就涉及如何解析SQL,并完成数据关联的逻辑。如如下SQL:
select a1 from ds_a on key=a where col1=xxx;
为了承载以上逻辑,我设计了下面的类和接口:

public class Rule {
    String table;
    List<String> fields;
    String key;
    ICondition cond;
}

interface ICondition {
    boolean filter(Map row);
}

public class Condition implement ICondition {
    String key;
    String value;

    @Overide
    public boolean filter(Map row){
        return row[key] == value;
    }
}

public class Conditions implement ICondition {
    List<ICondition> conds;
    String operation; //"and" 或者 "or"

    @Overide
    public boolean filter(Map row){
       if(operation == "and")
          return !has(false);
       else 
          return has(true);
   }
}

为了从SQL构建Rule,就需要对SQL字符串进行解析,想了好久,尝试了很多次,首先通过正则表达式提取出fileds,table,key,condition字符串

.*select (.*) from (.*) on (.*) where (.*)

前面3个字段还好处理,关键是condition字符串到 ICondition的映射,通过上述表达式已经提取到了condition字符串 col1=xxxx,如果只是这么简单也还好处理,split一下就可以得到相依的key和value,从而构造Condition作为rule的ICondition,但又想要支持更为复杂的condition,比如:

col1 = xxx and ( col2=xxx or col3=xxx )

这时候犯难了,又经过一番折腾最终实现了,其中过程就不详述了,只写下最终的思路:
1、首先对condition进行分词,将每一个不可分割的元素提取到数组中。
2、优先处理阔号,首先将括号的内容处理成ICondition,如果阔号存在嵌套,则最先处理里面的括号,把括号中的内容提取出来构建ICondition,由于这部分内容已经不包含括号,所以处理起来相对简单,我将不带括号的condition叫简单condition,通过这样的方式逐层剔除括号,最终转化成针对一个个简单condition的处理;
3、简单condition的处理要从高优先级运算到低优先级运算顺序处理,对于简单condition无法只剩下如下运算符: =,and,or,他们的优先级为 =优先于and, and 优先于 or。所以先把=号两侧的操作数构建成ICondition,然后再把and两侧的操作数构建成ICondition,然后才是or。

由于时间仓促,相关代码只能来日再补上,上面贴的代码只是为了示意,并非真实代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于 SQL 语句解析成表及字段,可以通过解析 SQL 语句中的关键词和语法结构来实现。 首先,需要使用语法解析器将 SQL 语句解析成语法树。然后,从语法树中提取出所有的表名和字段名,并将它们存储在一个数据结构中,例如一个列表或字典。 对于表名,可以在语法树中找到所有的表引用节点,然后提取出其中的表名。如果 SQL 语句中使用了表的别名,也需要将其解析出来并与原表名对应。 对于字段名,可以在语法树中找到所有的列引用节点,然后提取出其中的列名。如果 SQL 语句中使用了表的别名或者列的别名,也需要将其解析出来并与原表名或列名对应。 最后,将解析出来的表名和字段名组合成一个数据结构返回即可。 示例代码: ```python import sqlparse def parse_sql(sql): # 解析 SQL 语句 parsed = sqlparse.parse(sql)[0] # 提取出所有的表名和字段名 tables = [] columns = [] for token in parsed.flatten(): if token.ttype == sqlparse.tokens.Name: # 如果是表名或者列名 name = str(token) if '.' in name: # 如果是带有表名前缀的列名 table_name, column_name = name.split('.') columns.append((table_name, column_name)) else: # 如果是表名或者不带有表名前缀的列名 tables.append(name) elif token.ttype == sqlparse.tokens.Keyword and str(token).upper() == 'AS': # 如果是别名 name = str(token.parent) if '.' in name: # 如果是带有表名前缀的别名 table_name, column_name = name.split('.') columns.append((table_name, column_name)) else: # 如果是不带有表名前缀的别名 columns.append((tables[-1], name)) # 返回解析结果 return {'tables': tables, 'columns': columns} ``` 这个函数接受一个 SQL 语句作为输入,返回一个字典,包含解析出来的表名和字段名。例如: ```python >>> sql = 'SELECT foo.id, bar.name AS bar_name FROM foo JOIN bar ON foo.id = bar.id' >>> parse_sql(sql) {'tables': ['foo', 'bar'], 'columns': [('foo', 'id'), ('bar', 'name')]} ``` 这个例子中,解析出来的表名是 `foo` 和 `bar`,解析出来的字段名是 `(foo, id)` 和 `(bar, name)`。可以看到,解析出来的结果包含了表名和列名的对应关系。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值