最近要封裝資料庫介面,查詢的關鍵是where條件。如何讓不懂sql的使用者,通過某些介面,拼接出符合條件的sql,成為了乙個問題。
不論多複雜的where,應該只包括3種型別的元素,表示式與邏輯操作符及組合元素。
表示式,即形同:colname>=val,colname=val,colname like '%val%'等等的元素
邏輯操作符,即or,and
組合元素:即邏輯操作符+表示式,或邏輯操作符+表示式+組合元素組成的元素。這是一種遞迴的表達,不知道是否容易理解。
可以舉個簡單的例子:
colname1=val1 and colname2=val2是一種組合元素,它由表示式colname1=val1,colname2=val2,以及邏輯操作符and組成。
(colname1=val1 or colnam2=val2) and colname3=val3也是一種組合元素,它由組合元素(colname1=val1 or colnam2=val2) ,邏輯操作符and,和表示式colname3=val3組成。
這樣寫太複雜了,簡化一下。因為組合元素如果**其本質,最終還是由表示式和邏輯操作符組成。所以,where條件的最基本元素應該只有兩個,表示式和邏輯操作符。
我們將它看做乙個物件,node。
class node(object):
def __init__(self,key,val,logic,nodetype):
self.key = key
self.val = val
self.logic = logic
# 0 is logic node, 1 is normal node
self.type = nodetype
self.links = list()
def islogicnode(self):
return self.type == 1
它具有5個屬性。其中type表明它是邏輯操作符(1),還是表示式(0)。
如果是邏輯操作符,那麼它的logic會有值(or,and);如果是表示式,那麼key,val會被賦值(name='1'被拆分為,key="name='?'",val="1";方便我們傳入的引數進行過濾,避免sql注入之類的)
python中沒有指標的概念,所以我們用過list陣列,存放該節點的子節點列表。
我心目中的樹是這樣的:
樹的描述如下:
1.葉子節點一定是表示式(因為不可能存在乙個葉子節點是邏輯操作符的情況)
2.在一棵子樹中,如果子樹的高度》=2(那麼子樹的根節點一定是邏輯操作符)
那麼,明顯的。為了避免使用者錯誤的建造沒有子節點的邏輯節點,所以,我們拼接where的介面只對外提供兩個方法:
a.建立表示式節點的方法
b.傳入節點陣列(節點可以是邏輯節點,也可以是表示式節點),邏輯操作符,返回邏輯節點的方法
即:邏輯節點的生成,僅能通過傳入節點陣列+邏輯操作符的形式生成
最後一步,就是得到樹節點的根節點之後。如何還原成where語句呢?
這明顯就是後序遍歷了,如果對於二叉樹而言,就是左子樹,右子樹,根(最後的根上的邏輯操作符,負責將左右子樹的表示式連線起來);但是,因為我們的樹是乙個邏輯操作符節點,下面可能有多個表示式節點,即我們是n叉樹。
後序也很簡單,即:子樹->根進行遍歷。用遞迴實現即可。語言描述遍歷方式如下:
方法 遍歷節點方法(node):
如果節點是表示式節點:返回key,val拼接好的字串
如果節點是邏輯操作符節點:
定義結果陣列
取該節點的每乙個子節點:
邏輯操作符 連線 結果陣列
我們如何判斷該遞迴遍歷方法一定能夠結束?因為根據樹的描述,我們的葉子節點一定是表示式節點,所以絕對能夠觸發返回key,val拼接好的字串的方法。所以遞迴一定能夠結束。
下面是對於我的思路的實現:
#coding:utf-8
import copy
class node(object):
def __init__(self,key,val,logic,nodetype):
self.key = key
self.val = val
self.logic = logic
# 0 is logic node, 1 is normal node
self.type = nodetype
self.links = list()
def islogicnode(self):
return self.type == 1
class nodetree(object):
def createnormalnode(self,key,val):
node = node(key,val,none,0)
return node
# private method ,user can only generate normal node
def __createlogicnode(self,logic):
node = node(none,none,logic,1)
return node
def combinewheres(self,nodes,logic):
logicnode = self.__createlogicnode(logic)
for node in nodes:
#淺拷貝,即是node物件的引用
return logicnode
def transsql(self,node):
# invalid node tree
if node.islogicnode() and len(node.links) == 0:
return -1
if not node.islogicnode():
return node.key.replace('?',node.val)
if node.islogicnode():
sql = list()
for item in node.links:
if -1 in sql:
return -1
if len(sql) == 1:
return sql[0]
else:
logic = " "+node.logic+" "
return '('+logic.join(sql)+')'
if __name__ == '__main__':
#(name1='a' or name2='b' or name3='c') and name4 like '%u%'
tree = nodetree()
node1 = tree.createnormalnode("name1 = '?'","a")
node2 = tree.createnormalnode("name2 = '?'","b")
node3 = tree.createnormalnode("name3 = '?'","c")
node4 = tree.createnormalnode("name4 like '?'","%m%")
ornode = tree.combinewheres([node1,node2,node3],'or')
andnode = tree.combinewheres([ornode,node4],'and')
#generate sql
print '1:',tree.transsql(andnode)
#(name1='a' or name2='b' or name3='c') and name4 like '%u%' and (name5>='a' and name6>='b')
node5 = tree.createnormalnode("name5>='?'","a")
node6 = tree.createnormalnode("name6>='?","b")
andnode2 = tree.combinewheres([node5,node6],'and')
andnode3 = tree.combinewheres([andnode,andnode2],'and')
print '2:',tree.transsql(andnode3)
執行結果如下:
1: ((name1 = 'a' or name2 = 'b' or name3 = 'c') and name4 like '%m%')
2: (((name1 = 'a' or name2 = 'b' or name3 = 'c') and name4 like '%m%') and (name5>='a' and name6>='b))
雖然括號與人類相比,可能會多打。但是,整個where條件的優先順序是不會受到影響的。
哈夫曼樹樹生成及編碼解碼
哈夫曼編碼 time limit 1000 2000 ms c others memory limit 32768 32768 k c others problem description 由若干個值無重複的結點及其權值,建立相應的哈夫曼樹。在合併過程中,若出現權值相同的情況,則優先選取編號小的進行...
生成樹 冗餘及安全特性
一 生成樹分類 生成樹主要解決二層環路問題,因為三層上有ttl,基本上不存在環路問題。本部分主要介紹三種生成樹及特性 pvst rstp mstp 1 生成樹選舉規則 以下各項特性為越低越好 a bridge idv b spanning tree path cost 各種乙太網型別的cost如下 ...
最小及次小生成樹
poj1679 the unique mst 一道裸的次小生成樹 思路就是先求出最小生成樹然後列舉沒有用過的邊減去環中最大的邊求次小生成樹 附 include include include define maxn 105 define inf 0x3f3f3f3f using namespace ...