ORACLE+PYTHON實戰:復制A表數據到B表


      最近在學習python ,看到了pythod的oracle,不僅可以一次fetch多條,也可以一次insert多條,想寫一個復制A表數據到B表的程序來看看實際效率能不能提高。寫完發現,非常驚艷!效率提升了近一倍! 當然可能會認為這個沒有實際意義,其實不然。

     從A表復制數據到B表有很多中方法,一般直接insert即可:

  insert into tableA select * from tableB ;

   但是當數據量非常大時,到達上億水准的時候,這樣做就很郁悶了,因為本身會跑很慢,又看不到進度,偶爾還會被數據庫因為回滾段不夠而悲劇。

    所以,這種時候,我一般是用游標來做的:

declare
  v_num number ; 
begin
  v_num:=0 ;
  for v_cur in (select t.prod_inst_id , t.acc_num , t.user_name from cust30.prod_inst t where rownum <50000 ) loop 
  insert into test_prod_inst values (v_cur.prod_inst_id , v_cur.acc_num , v_cur.user_name) ;
  v_num:=v_num+1 ; 
  if mod(v_num,50000) = 0 then 
    commit ; 
  end if ; 
  end loop ;
end ;

  

 (也可以用fetch一次多條的方式:bulk  但是實際測試實際快不了多少)。現在的想法就是拿python替代這個,實際代碼如下:

     

#!/home/orashell/python27/bin/python
# -*- coding: utf-8 -*-
import os
import cx_Oracle

#需要設置這個不然插入中文會亂碼
os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8'
#目的數據庫
trans_to_db = cx_Oracle.connect('user/pass#@servicename')
#來源數據庫
trans_from_db = cx_Oracle.connect('user/pass#@servicename')
#打開查詢游標
curselect = trans_from_db.cursor()
#打開插入游標
curinsert = trans_to_db.cursor()

#根據游標生成插入的語句 需要根據已經打開的游標、目的表名 輸出這樣的
#insert into test_prod_inst (PROD_INST_ID,ACC_NUM,USER_NAME) values(:1,:2,:3)
#輸入 fromcur 為一個已經打開的游標對象
#輸入 totable 為目的表名
#輸出 returnstr 為生成好的SQL
def getInsertSql( fromcur ,totable ):
    #習慣這樣做 :) 先生成一個字符串模板 再替換
    returnstr = 'insert into '+totable+' (SELECTSTR) values(INSERTSTR)'
    # 得到游標的描述 cx_Oracle游標描述 本質為一個元組(見下) 第一列為字段名
    #[('PROD_INST_ID', <type 'cx_Oracle.NUMBER'>, 17, None, 16, 0, 0), ('ACC_NUM', <type 'cx_Oracle.STRING'>, 32, 96, None, None, 0), ('USER_NAME', <type 'cx_Oracle.STRING'>, 250, 750, None, None, 1)]
    curdesc = fromcur.description
    selectstr = ''
    insertstr = ''
    num=0
    #拼好字符串模板的 SELECTSTR 以及 INSERTSTR 部分
    for i in curdesc:
        num=num+1
        selectstr=selectstr+i[0]+','
        insertstr=insertstr+':'+str(num)+','
    #去掉最后一個','
    selectstr=selectstr[0:len(selectstr) - 1]
    insertstr=insertstr[0:len(insertstr) - 1]
    #替換
    returnstr=returnstr.replace('SELECTSTR',selectstr );
    returnstr=returnstr.replace('INSERTSTR',insertstr );
    return  returnstr
#實際執行的函數
def runmain():
    #用一個SQL生成游標
    curselect.execute('select  t.prod_inst_id , t.acc_num , t.user_name  from cust30.prod_inst t where rownum<10000 ')
    #得到插入游標的
    manyinserstr=getInsertSql(curselect, 'test_prod_inst')
    #插入游標 prepare
    curinsert.prepare(manyinserstr)
    while True:
        #fetch cx_Oracle fetch 當fetch 一條的時候 得到的是一行數據的元組 但是如果是多行 得到的是一個list
        #所以 fetchone的結果不轉換 不能使用executemany
        x=curselect.fetchmany(5000)
        #插入
        curinsert.executemany(None, x)
        #提交
        trans_to_db.commit()
        #判斷退出
        if len(x)==0:
            break

#執行
if __name__ == '__main__':
    runmain()
    trans_from_db.close
    trans_to_db.close

  

 

       本以為用這個會慢一些,因為實際上,這批數據庫是過了網絡的(數據-本機-數據庫),而使用PLSQL是沒有使用網絡。但是用這個插了5000萬數據,結果卻不是這樣,用了64秒,而用前文的第一種方式用了113秒,差不多是一倍的效率,這還是一個數據庫兩個表的復制,如果是兩個數據庫,跨dblink會更加明顯。

原因我猜測是這么兩個:

       A:在實際insert的時候,cx_Oralce拆成了多個線程去處理。如果考慮實際在特別大數據量的時候,plsql這邊也可以分為多個模處理,效率最終可能會卡在IO上。

       B:oracle的內存管理更加復雜,會比python這種相當於手動管理的方式,消耗的資源會更多。

       期待大神能夠解惑。

 

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM