前面講過 連接數據庫【一】,主要是去表裏Select;這次說下Insert;
autocommit 自動提交
在我印象中,使用pymysql庫 在表裏插入、修改、刪掉數據,好像都是要 先commit下,才能將數據寫入;
這兒說個 小技巧,是Connection類的autocommit :
先看下 Connection的註釋
:param autocommit: Autocommit mode. None means use server default. (default: False)
默認是Fasle; 我現在都是設置爲True:
db = pymysql.connect('x.x.x.x', 'zyooooxie', 'csdn', 'db', autocommit=True)
唯一的好處是 自動提交,不必每次執行sql後 要Commit() ;
情景一 創建訂單數據
說些實際應用:某供應商系統,要檢驗 某供應商 日結明細某些字段值 和 當天訂單的相關字段值的關係(類似 某天全部訂單交易手續費 和每筆交易的手續費的關係);那就要往庫表插入很多數據了;
先看下 字段【有省略】:
思路:
- vendor_id和vendor_name 肯定是固定的,order_id肯定是要不重複,receive_time爲 毫秒級時間戳,order_status肯定是【已簽收 | 已退款】;
- 從某天開始,到某天結束,連續造數據;start_date是實際訂單的最早時間,假設系統上線時間爲2020-01-01,後面一天才有訂單,故其爲2020-01-02;end_date是訂單的結束時間,一般做爲 當天的前一天(今天看 昨天);這2個時間點 當然也可以指定;
- 假設 價格 關係爲 settlement_price + fee = supply_price;
def insert_data(self, vendor_info, start_date=None, end_date=None):
if start_date is None:
st = '20200102'
else:
st = start_date
if end_date is None:
en = (datetime.date.today() - datetime.timedelta(days=1)).strftime('%Y%m%d')
else:
en = end_date
dt_list = self.date_list(st, en) # 某時間段
db = pymysql.connect('xie.xie.xie.xie', 'zyooooxie', 'csdn', 'xie', autocommit=True)
cur = db.cursor()
for vendor in vendor_info:
vendor_id = vendor[0]
vendor_name = vendor[1]
for dt in dt_list:
receive_time = int(round(1000 * (time.mktime(time.strptime(dt + ' 12:20:30', "%Y%m%d %H:%M:%S")))))
data_list = random.sample(range(100000, 999999), 3) # 隨機取3個不重複的值
for i in data_list:
# 訂單id爲 date + 某價格 【1. date不同 2. date相同,但價格不同;故不會出現相同訂單id】
order_id = ''.join([dt, str(i)])
settlement_price = i
supply_price = settlement_price + 50
fee = 50 # 寫死
sql = "INSERT INTO t_order (vendor_id, vendor_name, order_id, settlement_price, supply_price, fee, order_status, receive_time) VALUES ({}, '{}', {}, {}, {}, {}, 'zyooooxie', {});".format(vendor_id, vendor_name, order_id, settlement_price, supply_price, fee, receive_time)
cur.execute(sql)
time.sleep(0.01)
cur.close()
db.close()
此外,既然有簽收訂單,肯定有退款的單子咯,這兒說下 退款時間refund_time
refund_time bigint null comment '訂單退款時間(Refund Time)',
假設:除了 最後一天 退款時間爲當天(訂單時間只到昨天, 昨天的退款單子都是 當天退的),其他時間裏的訂單都是 後面一天退的;
if dt_list[-1] != dt:
new_dt_index = dt_list.index(dt) + 1
refund_dt = dt_list[new_dt_index]
else:
refund_dt = dt
refund_time = int(
round(1000 * (time.mktime(time.strptime(refund_dt + ' 17:16:15', "%Y%m%d %H:%M:%S")))))
這腳本用起來,確實節省太多人工;省了我很多力氣;
(上面講的是 插入order訂單,另外 插入recharge充值明細 + 插入daily report日結明細 都雷同;
各種數據生成後,需要fix,實際修復數據腳本的思路 也類似)
情景二 大數據量的insert
pymysql
在實際工作中,有時候可能會出現很大數據量,可能平均每天的訂單數量就超過十萬條,那怎麼樣插入數據 更快呢?
上面情景中,按我的代碼,大概每秒鐘 插入30-50條記錄;如何實現每秒插入數萬條數據呢?
executemany()就是爲此情景帶來希望,可實現 多行插入;
這兒只說一點經驗:
每個字段的字段值 都寫入 all_data 【VALUES() 裏面都是 %s,不能出現 某字段值】;
all_data = list()
all_data.append((vendor_id_name[0], vendor_id_name[1], 'IDR', order_id, settlement_price, supply_price, fee, 91, create_time, refund_time))
sql_1 = "INSERT INTO `t_order` (vendor_id, vendor_name, currency, order_id, settlement_price, supply_price, fee, status, receive_time, refund_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s);"
cur.executemany(sql_1, all_data)
憑啥不能寫成如下?
all_data = list()
all_data.append((vendor_id_name[0], vendor_id_name[1], order_id, settlement_price, supply_price, fee, create_time, refund_time))
sql_1 = "INSERT INTO `t_order` (vendor_id, vendor_name, currency, order_id, settlement_price, supply_price, fee, status, receive_time, refund_time) VALUES (%s, %s, 'IDR', %s, %s, %s, %s, 91, %s, %s);"
cur.executemany(sql_1, all_data)
因爲後面一種速度慢 = _ = 【我也不知道原因啦,沒有深入的學習】
sql語句
這兒說的是:一條sql語句插入多條數據;Insert into 表名 (某字段名a,某字段名b) Values (a1, b1), (a2, b2), (a3, a4);
正常連庫,插入數據,其實本不用這樣做,但某些情景下,只能用sql語句來做的時候,我覺着這樣做 相對一條一條insert語句去執行,會高效些;
【最近有個聯調環境,只能通過堡壘機去登錄後,再去訪問數據庫;運維同事不給開放連接數據庫的權限,我又偏偏要造大數據量,咋整呢?】
def test_0427(self, date_list, table, ssh_channel):
"""某時間段,每天插入40w,每條sql插入200條記錄"""
for d in date_list:
Log.info('時間爲 {}'.format(d))
kaishi = 1
jie = 400000
abc_list = range(kaishi, jie)
for st in range(0, math.ceil(len(abc_list) / 10000)):
Log.info('第{}個1w條數據'.format(st))
kai = kaishi + st * 10000
for abc in range(math.ceil(10000 / 200)):
Log.info('頁數 {}'.format(abc))
start = kai + abc * 200
end = 200 + start
day_data = list()
# Log.info(start)
# Log.info(end)
for a in range(start, end):
gateway_order_id = int(''.join([d, "{:0>6d}".format(a)]))
gateway_amount = random.choice([a, a, a, a * 35])
gateway_transaction_status = random.choice([1, 9, 2, 3, 4, 5, 6])
transaction_order_date_timestamp = int(d)
day_data.append((gateway_order_id, gateway_amount, gateway_transaction_status,
transaction_order_date_timestamp))
sql = "insert into {} (gateway_order_id, gateway_amount,gateway_transaction_status,transaction_order_date_timestamp) VALUES {};".format(
table, tuple(day_data)).replace('((', '(').replace('))', ')')
ssh_channel.send(sql)
ssh_channel.send('\r')
這樣做,insert 的速度 比起前一種差太多;而且sql語句是有SQL長度限制;還是推薦前面那一種來做。
交流技術 歡迎+QQ 153132336 zy
個人博客 https://blog.csdn.net/zyooooxie