python模拟SQL语句操作文件
1.需求
在文本界面输入SQL语句,查询相应的数据,例如输入下面的语句
print(''' 支持大小写的SQL语句查询,大写或者小写都可以 1. select * from db1.emp 2. select * from db1.emp limit 4 3. select * from db1.emp where id > 24 4. select * from db1.emp where name like 李 5. select * from db1.emp where id > 10 and id <14 or name like 李 6. SELECT * from db1.emp where not id > 24 7. INSERT into db1.emp values 张国辉,30,18676575678,运维,2007-8-1 8. delete from db1.emp where id > 25 9. update db1.emp set name='sb' where id=24 10.update db1.emp set name='alex' where name like sb ''') #db1.emp是一个数据库,名字随便取,只要能找到这个值就行
开始分析整个需求
1.根据输入的语句实现数据库中的增删改查,四个操作
①.语句解析 select, insert ,update,delete
②.四种语句解析做的工作类似,抽取一个函数专门解析
def handle_sql_parse(sql_l,sql_dic){}
解析完成之后返回解析的语句,在分发给对应的函数
③.拿到解析的SQL语句,调起相对应的函数(insert,delete,update,select),查询语句
④.返回查询到的结果,输出打印。
2.具体业务实现
1.入口函数
if __name__ == "__main__": while True: print(''' 支持大小写的SQL语句查询,大写或者小写都可以 1. select * from db1.emp 2. select * from db1.emp limit 4 3. select * from db1.emp where id > 24 4. select * from db1.emp where name like 李 5. select * from db1.emp where id > 10 and id <14 or name like 李 6. select * from db1.emp where not id > 24 7. insert into db1.emp values 张国辉,30,18676575678,运维,2007-8-1 8. delete from db1.emp where id > 25 9. update db1.emp set name='sb' where id=24 10.update db1.emp set name='alex' where name like sb ''') sql = input('sql>>:').strip() if sql == 'exit' or sql == 'q': break if len(sql) == 0:continue #开始解析sql语句 sql_dic = sql_parse(sql) print(sql_dic) # 拿到处理好的sql语句开始查询 result = sql_action(sql_dic) print('*'*30 + '查询结果' + '*'*30) for value in result[-1]: print(value) print('*'*30 + '查询完毕' + '*'*30)
2.根据select,insert,update,delete,四种语句进行分发,分别处理语句
def sql_parse(sql): ''' 输入sql语句,根据关键字拿到处理的函数,传入sql语句解析 :param sql: :return: ''' func_dict = { 'insert':insert_sql_parse, 'delete':delete_sql_parse, 'update':update_sql_parse, 'select':selete_sql_parse } #将sql语句以空格为界转化为一个列表 sql_l = sql.split(' ') #获取列表中的第一个值即为字典对应的key,命令可能为大写,全部转化为小写去执行 sql_order = sql_l[0].lower() sql_result = '' if sql_order in func_dict: sql_result = func_dict[sql_order](sql_l) return sql_result
3.具体的select,insert,update,delete四种方式对应的函数
def insert_sql_parse(sql_l): ''' insert语句,定义语法规则字典传入给语句解析函数 :param sql_l: :return: ''' # insert_action 定义的处理insert SQL语句的函数, sql_dic = { 'func': insert_action, 'insert': [], 'into': [], 'values': [], } return handle_sql_parse(sql_l, sql_dic) def delete_sql_parse(sql_l): ''' delete语句,定义语法规则字典传入给语句解析函数 :param sql_l: :return: ''' sql_dic = { 'func': delete_action, 'delete': [], 'from': [], 'where': [], } return handle_sql_parse(sql_l, sql_dic) def update_sql_parse(sql_l): ''' update语句,定义语法规则字典传入给语句解析函数 :param sql_l: :return: ''' sql_dic = { 'func': update_action, 'update': [], 'set': [], 'where': [], } return handle_sql_parse(sql_l, sql_dic) def select_sql_parse(sql_l): ''' select语句,定义语法规则字典传入给语句解析函数 :param sql_l: :return: ''' sql_dic = { 'func':select_action, 'select': [], 'from': [], 'where': [], 'limit': [], } return handle_sql_parse(sql_l,sql_dic)
4.四种语句有共同性,用一个handle函数统一处理返回处理后的sql语句,而且考虑到where后面的运算符处理,我们把逻辑处理的部分在拿出来封装一个函数去处理
def handle_sql_parse(sql_l,sql_dic): ''' 执行sql解析操作,返回sql_dic :param sql: :param sql_dic: :return: 参考字典 sql_dic = { 'func':select_action, 'select': [], 'from': [], 'where': [], 'limit': [], } ''' tag = False for item in sql_l: #对应的key都转换为小写 item = item.lower() #如果列表中的值和key对应,并且标志位也是true ,就把标志位置反 if tag and item in sql_dic: tag = False # 如果列表中的值和key对应,并且标志位也是false ,就把标志位置反,把对用的item赋值给key if not tag and item in sql_dic: tag = True key = item continue if tag: # 标识为true 就把对应的item放到上次循环中对应的key中 # 根据上一步的item 在字典中取出对应的列表添加元素 # 以select为例,输入select * from db1.emp where id > 24 # 返回{'func': <function select_action at 0x10343f598>, 'select': ['*'], 'from': ['db1.emp'], 'where': ['id', '>', '24'], 'limit': []} sql_dic[key].append(item) if sql_dic.get('where'): # 单独处理where,返回之后的字典,就是把where中的比较字符每一个条件处理成了一个数组 #{'func': < function select_action at 0x102ecf598 >, 'select': ['*'], 'from': ['db1.emp'], 'where': [['id', '>', '24']], 'limit': []} sql_dic['where'] = sql_where_parse(sql_dic.get('where')) return sql_dic
5.单独对where语句和其中的逻辑运算语句的处理
def sql_where_parse(sql_where_l): ''' 处理where语句 <class 'list'>: ['id', '>', '10', 'and', 'id', '<14', 'or', 'name', 'like', '李'] :param sql_where_l: :return: ''' result = [] key = ['and','or','not'] char = '' #拼接字符显示 for value in sql_where_l: if len(value) == 0:continue # 如果是逻辑运算 if value in key: if len(char) != 0: char = logic_parse(char) result.append(char) result.append(value) char = '' #每次都清空 else: # 先处理 and or not 这个三个特殊的逻辑运算符,例如上面的例子中,先把id>10作为一个整体放入char中,遍历到and的时候再对char处理 # 调用logic_parse()函数进行处理,将id>10处理为一个列表['id','>','10'],将列表添加到外围的列表中,同时添加逻辑运算符,清空char # 再次运行 char+=value else: # 处理最后一个char字符,变为列表 char = logic_parse(char) result.append(char) return result
6.逻辑运算符的解析
def logic_parse(logic_str): ''' 对where中的逻辑运算做处理,返回一个字典,将一个小的条件过滤 例如 age>10 转换成 ['age','>','10'] :param login_str: :return: ''' key = ['>','<','='] result = [] char = '' #拼接运算符 flag = False #添加的标识位 option = '' #记录是否是运算符 for value in logic_str: # age>10,类似于这样的字符串,逐个处理,如果不是运算符,就拼接字符,直到是运算符为止, # 将字符添加到数组中,再次判断是否是运算符,拼接运算符,如果不是运算符 就将运算符也添加到数组中,依次循环 if value in key: flag = True if len(char) != 0: result.append(char) char = '' option+= value if not flag: char+=value if flag and value not in key: flag = False result.append(option) option = '' char+=value else: result.append(char) # 添加对like的解析 if len(result) == 1: # 以like为分割线,转化为数组, result = result[0].split('like') # 在中间位置添加like result.insert(1,'like') return result
7.在入口函数中拿到解析后的sql语句,开始执行对应的select,update,delete,insert语句,对这个语句做一个分发的操作
def sql_action(sql_dic): ''' 字典中提取命令 分发给对应的具体指令去执行 :param sql_dic: :return: 根据传入的字典,调用对用的方法, sql_dic = { 'func':select_action, 'select': [], 'from': [], 'where': [], 'limit': [], } ''' return sql_dic.get('func')(sql_dic)
8.编写对应的insert,select,update,insert等语句开始执行
def select_action(sql_dic): ''' 执行select 语句 :param sql_dic: :return: ''' # 解析路径 if '.' in sql_dic['from'][0]: file_path = sql_dic['from'][0].replace('.','/') else: file_path = sql_dic['from'][0] # print(path,table) filehandle = open(file_path,'r',encoding='utf-8') #分别去解析where limit 还要对比显示文字 where_resutl = where_action(filehandle,sql_dic['where']) filehandle.close() # print(where_resutl) #添加limit操作 limit_result = limit_action(where_resutl,sql_dic['limit']) #根据搜索添加结果 search_result = search_action(limit_result,sql_dic['select']) return search_result
9.根据解析后的sql语句 还需要对相应的where字符做处理,添加对应的where和limit,最后加上select的处理
def where_action(filehandler,whele_sql): ''' 解析where语句,显示where语句中的执行条件 :param filehandler: 文件句柄 :param whele_sql: :return: ''' result = [] titles = 'id,name,age,phone,dept,enroll_data'.split(',') if len(whele_sql) != 0: for line in filehandler: # zip 转化为一个元祖,如果两个队列长度不一致,以短的返回一个元祖 #tem_dict={'id': '1', 'name': '姬建明', 'age': '25', 'phone': '15201541043', 'dept': '运维', 'enroll_data': '2013-11-01\n'} tem_dic = dict(zip(titles,line.split(','))) # 处理逻辑运算符中的数据 logic_res = logic_action(tem_dic,whele_sql) # 返回为true的时候,添加数据到列表中 if logic_res: result.append(line.split(',')) else: return filehandler.readlines() return result def logic_action(dic,where_sql): ''' 根据字典和sql语句进行匹配,匹配成功返回true 匹配失败返回false :param dic: :param where_sql: :return: ''' # [['id', '>', '10'], 'and', ['id', '<', '14'], 'or', ['name', 'like', '李']] result = [] for item in where_sql: # 过滤掉and or not 等逻辑运算符 if type(item) is list: # print(item,dic) prop,option,value = item # 如果option为= 在后面在拼接一个= if option == '=': option+='=' # 判断是否是数字 if dic[prop].isdigit(): dic_v = int(dic[prop]) value = int(value) else: dic_v = '%s' %dic[prop] if option != 'like': # 将字符串str当成有效的表达式来求值并返回计算结果 flag = str(eval("%s%s%s" %(dic_v,option,value))) else: if value in dic_v: flag = 'True' else: flag = 'False' # 拼接每一个匹配的值, result.append(flag) # 添加and进行连接 result.append('and') #删除最后一个and result.pop() # 计算数组中的表达式的值 res = eval(' '.join(result)) # 将result中的bool值进行处理,并计算所有bool值的最后结果 return res
10.添加limit处理
def limit_action(where_res,limit_l): ''' 根据用户输入的limit 返回limit的个数的数组 :param where_res: :param limit_l: :return: ''' # 根据传入的limit 做一个切片处理 if len(limit_l) != 0: return where_res[0:int(limit_l[0])] else: return where_res
11.处理select中除了*之外的其他字符 select id name IT from*****. 语句只返回id name IT这个三个属性
def search_action(limit_res,select_l): ''' 根据用户的输入,返回特定的数组 :param limit_res: :param select_l: :return: ''' result = [] fileds_l = [] titles = 'id,name,age,phone,dept,enroll_data'.split(',') if select_l[0] == "*": fileds_l = titles result = limit_res else: # 循环匹配where和limit语句的执行结果 for item in limit_res: # zip 转化为一个元祖,如果两个队列长度不一致,以短的返回一个元祖 #{'id': '25', 'name': '张国辉', 'age': '30', 'phone': '18500841678', 'dept': '运维', 'enroll_data': '2007-8-1\n'} temp_dict = dict(zip(titles,item)) condition_l = [] fileds_l = select_l for i in fileds_l: condition_l.append(temp_dict[i].strip()) result.append(condition_l) #只返回搜索的字段名 return (fileds_l,result)
12.删除操作delete
def delete_action(sql_dic): ''' 删除表中的元素,返回删除成功标识 :param sql_dic: :return: ''' # 'func': delete_action, # 'delete': [], # 'from': [], # 'where': [], if '.' in sql_dic['from'][0]: file_path = sql_dic['from'][0].replace('.','/') else: file_path = sql_dic['from'][0] titles = "id,name,age,phone,dept,enroll_date".split(',') # 创建一个临时文件复制指定文件的内容,完成之后,删除源文件,修改临时文件的名称 with open(file_path,'r',encoding='utf-8') as r_obj,\ open("%s.txt" %file_path,'w',encoding='utf-8') as w_obj: for item in r_obj: tem_dic = dict(zip(titles,item.split(','))) flag = logic_action(tem_dic,sql_dic['where']) if not flag: w_obj.write(item) w_obj.flush()#立刻从内存刷到硬盘上 os.remove(file_path) os.rename("%s.txt" %file_path,file_path) return [['删除成功']]
13.更新操作update
def update_action(sql_dic): ''' 更新对应的数据 'func': update_action, 'update': [], 'set': [], 'where': [], :param sql_dic: :return: ''' if '.' in sql_dic['update'][0]: file_path = sql_dic['update'][0].replace('.','/') else: file_path = sql_dic['update'][0] set_sql = sql_dic['set'][0].split(',') sets = [] for value in set_sql: sets.append(value.split('=')) titles = "id,name,age,phone,dept,enroll_date".split(',') with open(file_path,'r',encoding='utf-8') as r_obj,\ open('%s.txt' %file_path,'w',encoding='utf-8') as w_obj: # 遍历文件内容 for item in r_obj: changeline = [] # 将每组数据格式化为一个字典 temp_dic = dict(zip(titles,item.split(','))) # 匹配字典中的值是否和where语句中的值相同 flag = logic_action(temp_dic,sql_dic['where']) if flag: #在数据库中存在这个数据,拿到数据修改为要设置的值 for set in sets: k = set[0] v = set[-1].strip("'") temp_dic[k] = v for prop in titles: changeline.append(temp_dic[prop]) # 数组转化为字符串,保存到文件中 w_obj.write(','.join(changeline)) w_obj.flush() os.remove(file_path) os.rename("%s.txt" % file_path, file_path) return [['修改成功']]
14.insert插入操作
def insert_action(sql_dic): ''' 'func': insert_action, 'insert': [], 'into': [], 'values': [], 插入新的元素,先要获取最后一行的数据,获取到他的ID,自增长ID :param sql_dic: :return: ''' if '.' in sql_dic['into'][0]: file_path = sql_dic['into'][0].replace('.','/') else: file_path = sql_dic['into'][0] print(file_path) titles = "id,name,age,phone,dept,enroll_date".split(',') with open(file_path,'ab+') as f_obj: # 查询文件的最后一行 offset = -50 while True: # seek(offset,可选值) 可选值默认为0 从文件开头算 1 从当前位置开始算 2从文件末尾算 f_obj.seek(offset,2) lines = f_obj.readlines() if len(lines) > 1: last_line = lines[-1] break offset *= 2 # 以二进制打开的文件,要对字符串解码 last_line = last_line.decode(encoding='utf-8') #取出最后一行的ID temp_dict = dict(zip(titles,last_line.split(','))) last_id = int(temp_dict['id']) #id加一 添加到数组中 add_info = sql_dic['values'] add_info.insert(0,str(last_id+1)) # 数组转化为字符串 new_line = ','.join(add_info) + '\n' f_obj.write(new_line.encode('utf-8')) f_obj.flush() return [['添加成功']]
因为添加的数据库格式固定了,还可以添加不知道字段名的数据库进行操作,全局修改titles这个数组来匹配所有的字段名,也可以手动输入,在进行解析,实现一个多个字段可用的SQL查询小工具