用pymysql做批量insert操作时,一般是用list第一个元素进行sql语句拼装,比如:
INSERT INTO order_status (code, description) values (%s, %s)
上述语句中的字段名code和description是根据0号元素的key值用代码拼装出来的:
# 拼接insert字符串
def __concatInsertSqlStr(self, table_name, dict_list, insert_str = 'INSERT'):
dict_key = dict_list[0]
fields_str = ''
bind_str = ''
for field_name in dict_key.keys(): # 字段名
if len(fields_str) == 0:
fields_str = '(' + field_name
bind_str = 'values(%s'
else:
fields_str = fields_str + ',' + field_name
bind_str = bind_str + ',%s'
sql_str = insert_str + ' INTO ' + table_name + ' ' + fields_str + ') ' + bind_str + ')'
return sql_str
如此,即可封装一个通用的list批量insert函数。
但是,可能出现两个难缠的问题。
第一个:220报错,比较明确
错误220 not enough arguments for format string
这个问题错误原因比较明确,就是因为list中的某个元素key值数量不对,多了或少了。因为python的字典list太过灵活,在list量大的时候很难查。
上面这个错误是由这个语句引发的:
r_log = [{'code': '9', 'description': '描述1'}, {'code': '7', }]
ret = dbopr_pool.insertDictList('order_status', r_log)
r_log数组list的1号元素少了description键值。
要解决这个问题,只需要找出键值数不一致的元素即可。
第二个:没有报错,但是结果不对
这个问题很难发现,因为insert语句成功了,也插入预期的记录数,但是字段值不对。
原因是,list各元素的键值顺序不一致,而拼接insert语句是基于0号元素的字典键值顺序。同时,pymysql会进行数据类型的强制转换,因此尽管可能数据类型不匹配,但也会被转换为目标数据类型。
例如:
r_log = [{'code': '9', 'description': '描述1'}, {'description': '描述2', 'code': '7', }]
ret = dbopr_pool.insertDictList('order_status', r_log)
r_log数组list的1号元素键值顺序与0号元素不同。这个语句insert结果不会出错,也成功插入了两条记录,但是第二条记录的字段值全错了。
解决方法
要解决这两个问题,只需要预先对list进行“整理”:
以0号元素为基准,检查各元素键值是否都与0号元素一致;
对各元素按键值进行排序。
代码如下:
def __preProcDictList(self, dict_list):
if (type(dict_list) != type([])) or (dict_list == None) or (len(dict_list) < 1):
return None
new_dict_list = []
sorted_key = sorted(dict_list[0])
for dl in dict_list:
if len(dl) != len(dict_list[0]):
return None
new_dl = {}
for key in sorted_key:
if key not in dl:
return None
else:
new_dl[key] = dl[key]
new_dict_list.append(new_dl)
return new_dict_list
整理好list之后,再按照0号元素key值进行insert语句拼接。
搞定收工!