Python腳本之連接數據庫【二】

前面講過 連接數據庫【一】,主要是去表裏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() ;

情景一 創建訂單數據

說些實際應用:某供應商系統,要檢驗 某供應商 日結明細某些字段值 和 當天訂單的相關字段值的關係(類似 某天全部訂單交易手續費 和每筆交易的手續費的關係);那就要往庫表插入很多數據了;

先看下 字段【有省略】:

在這裏插入圖片描述

思路:

  1. vendor_id和vendor_name 肯定是固定的,order_id肯定是要不重複,receive_time爲 毫秒級時間戳,order_status肯定是【已簽收 | 已退款】;
  2. 從某天開始,到某天結束,連續造數據;start_date是實際訂單的最早時間,假設系統上線時間爲2020-01-01,後面一天才有訂單,故其爲2020-01-02;end_date是訂單的結束時間,一般做爲 當天的前一天(今天看 昨天);這2個時間點 當然也可以指定;
  3. 假設 價格 關係爲 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章