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 替換\t \n \r 
        hql = $.trim(hql.replace(/[\n|\r]/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我...

感興趣或者正在做這塊,大家一塊討論.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM