• SQL编辑器自动提醒实现


      市场上已经有很多成熟的数据库客户端工具。像toad、navicat等都可以很方便的访问、查询数据库。这种工具最核心的作用就是连接数据库、添删改查表或表记录,使用率最高的应该是查询功能,查询最得意的地方就是提供自动提醒功能..输入表名后立即会出现表字段的列表。

    极大提高了写sql的效率。当你希望把这些功能搬到web上时,可能在实现上会遇到问题。比如怎么获取表名、别名,字段别名、子查询等等。最近在开发对hive的ad-hoc工具,web页面支持用户编写sql进行查询.演示效果不错。用户也提出了新的需求,比如已经用习惯的功能自动提醒能不能在这工具上实现。回到工作中,开始研究..

    1、 准备工作:

    1.1 准备2个ajax方法..1、通过数据库名获得所有表名 2、通过表名获得表字段 

    1.2 前端我是使用了ace.js一个代码编辑器,这个后续单独介绍,关键的是它提供了提示数据库关键字的方法,我们可以改变传入的keyword实现提醒,修改js:ext-language_tools.js

    2、 怎样获取表名,在这里的表名有几种情况:实实在在的表名、表名后的别名、子查询后的别名

    实现思路:

    // 定义2个数组和conf

    var realy_table = {}; // {table/db xxx,xxx,xxx,xxx}
    var alias_relation = {};// 别名 {别名:真实名字}
    var realy_table_id={}; // 存储表的id标识

    var conf = {
      db_url:"",
      table_url:"xxxx",
      col_url:"xxxxx"
    };



    当用户输入"."时,触发脚本, 获取“.”前面的字符

    /**
     * query 
     * pos 点的位置
     * select a.b   ,    a.d from dwa.xxx a;
     * 当分析失败,返回null,不进行自动提醒
     */
    function getPointFontWord(query, pos) {
        var reg = /s{2,}/g;
        var res = "";
        var alias = getNameByPos(query, pos, res);
        return alias;
    }
    
    // o, pos.column
    // 从当前位置往前找,遇到空格或“,”号说明遍历完成
    function getNameByPos(query, pos, res) {
        var be = query.substring(pos, pos-1);
        if(pos <= 0) {
            return res;
        }
        if(be==" " || be == ",") {
            return res;
        } else {
            res = be + res;
            return getNameByPos(query, pos-1, res);
        }
    }

    如果为数据库,首先判断是否获取过,否则调用之前准备的ajax方法获取表组装之前定义的数组

    if("你的数据库名" == a1) { // 数据库
    // 判断是否获取过
                    if(alias_relation[al] != undefined && realy_table[alias_relation[al]] != undefined) {
                        console.log("db cache...");
                        tbs = realy_table[alias_relation[al]];
                    }  else {
                        tbs = getTableByDb(al); // 远程获取、组装、返回
                    }
    }
    // 远程获取、组装、返回

    function getTableByDb(db) {
        if(realy_table[db] != undefined) 
            return realy_table[db];
        getHiveTableByDb(db);
        return realy_table[db];
        
    }
    
    /**
     * get table
     * 数组1 id <=>表名
     * 数组2 表名<=>字段 或 db<=>表名
     * 
     * */
    function getHiveTableByDb(db) {
        jquery.ajax({
              url : conf.table_url,
              type : 'POST',
              async : false,
              data : db,
              ok : function(res, textStatus, jqXHR) {
                  var tas = res.data;
                  var tables="";
                  for(var j=0;j<tas.length;j++){
                      tid = tas[j].TBL_ID;
                      tname = tas[j].TBL_NAME;
                      alias_relation[tname] = tname;
                      realy_table_id[tname] = tid;
                      tables += tname + ",";
                  }
                  if(tables.indexOf(",")>-1)
                      tables = tables.substring(0,tables.length-1);
                  if(tables != "")
                      alias_relation[db] = db;
                      realy_table[db] = tables;
              }
        });
    }

    如果不是数据库名

    // 不是db,那就是别名或者表名
                    var hql = session.getValue();
                    // 已存在的缓存
                    if(alias_relation[al] != undefined && realy_table[alias_relation[al]] != undefined) {
                        console.log("table cache...");
                        tbs = realy_table[alias_relation[al]];
                    } else {
                        tbs = findColumnsByKey(hql, al);
                    }
        
                    try {
                        tbs = realy_table[alias_relation[al]];
                    } catch(e) {
                    }

    查找表名、别名对应的column,获取后组装到数组中....

    /**
     * 一输入"点"就开始重置keyword,先获得前面的key,
     * 然后indexOf( key ),然后从这个位置往前找..
     * 1. 如果第一个字符为) 说明为子查询
     * 2. 如果不是则是单表查询..
     */
    function findColumnsByKey(hql, key) {
        try {
            // 首先格式化query 替换	 
     
     
            hql = $.trim(hql.replace(/[
    |
    ]/g,' '));
            var reg = /s{2,}/g;
            // 多个空格变成一个
            hql = hql.replace(reg, " ");
         // 获得表别名的位置
    var tabInx = getTabInx(hql, key); var inx = tabInx.inx; var t_key = tabInx.key; var c_h = $.trim(hql.substring(0, inx)); var t_h = $.trim(hql.substring(0, inx+t_key.length));
        // 判断是普通查询还是子查询  isSub
    = checkType(c_h.substring(c_h.length-1, c_h.length), ")"); if(isSub) { // 子查询 var res = ""; c_h = c_h.replaceAll("\( ","\(").replaceAll(" \)",")"); res = $.trim(getSub(c_h, c_h.length, res)); res = $.trim(res.substring(0, res.length-1)); findTable2(res, key); } else { var fromlen = t_h.replaceAll("from", "FROM").lastIndexOf("FROM"); var t_tab = $.trim(t_h.substring(fromlen + 4, t_h.length)); makeT(t_tab.split(" ")); return realy_table[alias_relation[key]]; } }catch(e) { console.log("find columns by key is error..return"); } }

     实现效果:

    整体思路就是这样的..js的效率估计不高..功能可以实现,慢慢优化..

    遗留问题:

      现在单纯的子查询可以实现提醒,如 select  a. from (select c1,c2,c3 from tb1) a

      考虑到效率和体验,使用纯js提醒不依赖于后端,所以像sql解析的开源代码没利用上,稍复杂的子查询提醒未实现.比如.当子查询中有函数的使用然后再别名时,提醒子查询的字段失效.. 

      如 select subquery.   from (select udf_a(col1,col2,col3) as col4 from abc) subquery,有好的方法或思路hi我...

    感兴趣或者正在做这块,大家一块讨论.

    工作方向: 大数据、数据仓库、 hadoop、hive、Hbase、 python、ad-hoc、scala、数据工具研发
    邮 箱    :zhangkai081@gmail.com
  • 相关阅读:
    理解并使用.NET 4.5中的HttpClient
    响应式图片的3种解决方案
    Rest模式get,put,post,delete含义与区别
    Multiple websites on single instance of IIS
    C# 消息队列-MSMQ
    微服务 Micro services
    SQL Server For XML
    Real-time chart using ASP.NET Core and WebSocket
    ASP.NET Core Building chat room using WebSocket
    Ajax 调用的WCF
  • 原文地址:https://www.cnblogs.com/smallbaby/p/SQL_IDE.html
Copyright © 2020-2023  润新知