實踐2-1 python連接Oracle數據庫


實踐2-1 python連接數據庫

java連接mysql數據庫在:實踐 1-1 JDBC使用詳解

這部分包含:python連接Oracle、以及window定時任務schtasks的一些簡單設置。

1、python連接Oracle數據庫

1.1 oracle連接環境配置

注意:1.1部分的環境配置僅僅是其中一個我自己使用的方法,在1.2中會包含官網中使用代碼指定oracle_client的方法。

一般來說,在你本地配置plsql訪問oracle的環境,配置成功后,再運行python連接oracle數據庫就沒問題了。

在配置之前,ping一下oracle的ip,確定網絡是互通的。然后安裝好合適版本位數的plsql。

由於遠端oracle並非自己配置的,訪問的驅動、配置文件等也是別人給的,而且還有點亂七八糟,所以有關版本的問題個人也不是很清楚。

按照教程所說:"到oracle官網下載instantclient basic包,解壓縮到E:\ProgramFile\instantclient。"

1.1.1 環境變量配置

我是按圖索驥,如果之前配置成功過,可以打開配置好的plsql(可以處於未登錄狀態),查看幫助-支持信息,以及電腦中相關的環境變量在新環境中進行如下配置。

設置環境變量如下,

Path=...;E:\ProgramFile\instantclient
NLS_lANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
TNS_ADMIN= E:\ProgramFile\instantclient
ORACLE_HOME= E:\ProgramFile\instantclient

即:

  1. 將instantclient目錄添加到Path,

  2. NLS_lANG配置則是防止中文亂碼等情況的出現,

  3. TNS_ADMIN指向tnsnames.ora所在目錄,

  4. ORACLE_HOME我就不怎么確定了,因為我的oci.dll和tnsnames.ora在兩個目錄中,plsql的安裝目錄又是另一個(我不想管了,能跑就行),我ORACLE_HOME指向的是oci.dll所在的目錄,但是后面涉及的某個錯誤中描述的是"在環境變量中增加 ORACLE_HOME 指向64位安裝目錄,而不是 instantclient 目錄"

1.1.2 PlSQL 配置

中文版進入“工具”-“首選項”-oracle“連接”,將oci.dll所在目錄及oci.dll文件路徑填入下面紅框框的位置,一般是

/* Oracle主目錄 */
E:\ProgramFile\instantclient
/* OCI庫 */
E:\ProgramFile\instantclient\oci.dll

 

 

1.1.3 配置tnsnames.ora文件

這個文件需要配置,包含你連接數據庫的ip、端口(1521)及服務名等信息。可配置多個連接,也可以只配置一個

# tnsnames.ora Network Configuration File: 文件目錄\tnsnames.ora
# Generated by Oracle configuration tools.
​
​
連接名1 =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 數據庫ip1)(PORT = 1521))
    (ADDRESS = (PROTOCOL = TCP)(HOST = 數據庫ip2)(PORT = 1521))
    (LOAD_BALANCE = yes)
    (FAILOVER = ON)
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = 服務名)
    )
  )
連接名2 =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 數據庫ip)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = 服務名)
    ))
​

 

1.1.4 一些報錯

一般來說是配置過程出錯的原因,出錯首先檢查配置過程,過程沒問題再考慮其他,僅僅記錄一部分

  • 登錄界面連接為空:tnsnames.ora未正確配置,好像無法連接遠端還會報錯'tns....'之類的

  • 登錄之后界面正常,但是select的結果界面為空,無論訪問任何表(甚至dual)都是這樣子。初步懷疑是版本問題,所以后面我按照之前的方法將兩個文件包都配置進去了。

  • Error while trying to retrieve text for error,如下

1.1.5 [報錯] Error while trying to retrieve text for error

Error while trying to retrieve text for error ORA-01019 的解決辦法

這個問題出現在我未配置ORACLE_HOME的時候,說是plsql客戶端和oracle服務之間32位與64位的沖突,給出的解決方法是“在環境變量中增加 ORACLE_HOME 指向64位安裝目錄,而不是 instantclient 目錄。”以及最后根本的更換下載的instantclient basic包的版本。

我自己的解決方法(由於之前配置成功過,但是相關的文件包比較亂),將ORACLE_HOME指向合適版本的oci.dll所在目錄。

1.2 cx_Oracle

cx_Oracle的主頁為:https://oracle.github.io/python-cx_Oracle/index.html

其開發文檔為:https://cx-oracle.readthedocs.io/en/latest/index.html

1.2.1 安裝與配置

若是該命令無法安裝,可下載相關安裝包后手動配置安裝,也可以添加代理。

pip install cx_Oracle
python -m pip install cx_Oracle --proxy=http://proxy.example.com:80 --upgrade

 

在window上,提供了兩種配置方法,一種如1.1部分一樣,在自己本地配置好一個可用的客戶端,配置好環境變量。還有一個是配置Oracle即時客戶端。具體可看:https://cx-oracle.readthedocs.io/en/latest/user_guide/installation.html#installing-cx-oracle-on-windows

  • 在 Windows 上,cx_Oracle 按如下方式查找 Oracle 客戶端庫:

    • lib_dir調用中指定的目錄中 cx_Oracle.init_oracle_client()。此目錄應包含解壓縮的 Instant Client 'Basic' 或 'Basic Light' 包中的庫。如果您從完整客戶端或數據庫安裝(例如 Oracle 數據庫“XE”快捷版)傳遞庫目錄,則您需要事先設置環境以使用該軟件安裝,否則將找不到消息文件等文件. 在 Windows 上,當路徑包含反斜杠時,請使用“原始”字符串,如lib_dir=r"C:\instantclient_19_6". 如果無法從 加載 Oracle 客戶端庫lib_dir,則會引發異常。

    • 如果lib_dir未指定,則在安裝 cx_Oracle 二進制模塊的目錄中查找 Oracle 客戶端庫。此目錄應包含解壓縮的 Instant Client 'Basic' 或 'Basic Light' 包中的庫。如果未找到庫,則不會引發異常並繼續搜索,請參閱下一個要點。

    • 在系統庫搜索路徑上的目錄中,例如PATH 環境變量。如果無法加載 Oracle 客戶端庫,則會引發異常。

import cx_Oracle
cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11")

 

如果要使用可選的Oracle配置文件,如tnsnames.ora,selnet.ora或oraacess.xml與即時客戶端,將對應文件放在某個可訪問的路徑下,如c:\oracle\dir,然后在代碼中指定。

import cx_Oracle
cx_Oracle.init_oracle_client(lib_dir=r"C:\oracle\instantclient_19_11",
                             config_dir=r"C:\oracle\your_config_dir")

 

1.2.2 數據庫連接

cx_Oracle進行數據庫連接的方式有兩種,獨立連接與連接池

獨立連接由cx_Oracle.connect()或其別名創建cx_Oracle.Connection(),其參數如下所示

cx_Oracle.connect(user=None, password=None, dsn=None, mode=cx_Oracle.DEFAULT_AUTH, handle=0, pool=None, threaded=False, events=False, cclass=None, purity=cx_Oracle.ATTR_PURITY_DEFAULT, newpassword=None, encoding=None, nencoding=None, edition=None, appcontext=[], tag=None, matchanytag=None, shardingkey=[], supershardingkey=[])

 

比較常用的參數有user,password,dsn,其中dsn有以下幾種形式:

import cx_Oracle
# 簡單連接
dbconn = cx_Oracle.connect(user='myuser',password='mypsw',dns='127.0.0.1\sname') #使用默認端口1521
dbconn = cx_Oracle.connect(user='myuser',password='mypsw',dns='127.0.0.1:1521\sname') #指定端口
# 通過配置好的dns連接
# 方法1:通過封裝的方法配置dhs
dns=cx_Oracle.makedsn("127.0.0.1", 1521, service_name="sname")
dbconn = cx_Oracle.connect(user='myuser',password='mypsw',dns=dns,encoding="UTF-8")
# 方法2:直接配置
dsn = """(DESCRIPTION=
             (FAILOVER=on)
             (ADDRESS_LIST=
               (ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521))
               (ADDRESS=(PROTOCOL=tcp)(HOST=196.121.78.3)(PORT=1521)))
             (CONNECT_DATA=(SERVICE_NAME=sname)))"""
​
dbconn = cx_Oracle.connect(user='myuser',password='mypsw',dns=dns,encoding="UTF-8")
# 方法3:將dns的配置放在tnsnames.ora文件中,如上面1.1.3中配置的“連接1”
dbconn = cx_Oracle.connect(user='myuser',password='mypsw',dns='連接1',encoding="UTF-8")
​

 

其他的參數如threaded在使用多線程訪問數據庫的時候(並沒有設置池),有設置為true,其具體功能並未深究。

而數據庫在使用完畢的時候需要關閉數據庫,可以使用dbconn.close(),或者說更建議使用with語句

with cx_Oracle.connect(user=user, password=password,
                       dsn="dbhost.example.com/orclpdb1",
                       encoding="UTF-8") as connection:
    cursor = connection.cursor()
    cursor.execute("insert into SomeTable values (:1, :2)",
                   (1, "Some string"))
    connection.commit()

 

當應用程序頻繁連接與斷開與數據庫連接時,連接池對於性能很重要。池支持Oracle的高可用性特性,建議用於必須可靠的應用程序。池是cx_Oracle.SessionPool()在應用程序初始化時創建的。連接池具體的使用留到后面多線程訪問的時候整理。

更加詳細的可看:連接到Oracle數據庫

1.2.3 數據庫操作

執行SQL語句是Python應用程序與Oracle數據庫通信的主要方式。語句是使用方法Cursor.execute()或執行Cursor.executemany()。語句包括查詢、數據庫操作語音(DML)和數據定義語言(DLL)。也可以執行一些特殊語句。

SQL語句不應包含尾隨分號;或正斜杠/

1.2.3.1 SQL查詢

查詢只能使用Cursor.execute()執行,可通過迭代行,或者可以使用Cursor.fetchone()、Cursor.fetchmany()或Cursor.fetchall()來獲取數據。

其中光標Cursor由Connect.cursor()返回,用於執行sql語句。光標使用結束后需要確保關閉,可以使用close()方法或者with語句。

import cx_Oracle
​
dbconn = cx_Oracle.connect(user='myuser',password='mypsw',dns='127.0.0.1:1521\sname') 
​
cur = dbconn.cursor() #創建光標
# 方法1 迭代行
for row in cur.execute('select * from my_table')
    print(row)
# 方法2 fetchone
cur.execute('select * from my_table')
while True:
    rs = cur.fetchone()
    if rs==None:
        break
     print(rs)
# 方法3 fetchmany--返回指定行數的數據
cur.execute('select * from my_table')
row_nums=10
while True:
    rows = cur.fetchmany(row_nums)
    if not rows:
        break
     for r in rows:
        print(r)
# 方法4 fetchall --返回全部數據
cur.execute('select * from my_table')
rows = cur.fetchall()
for r in rows:
    print(r)
    
cur.close()#關閉光標

 

SQL查詢還可以查詢列名和數據類型。

with connection.cursor() as cur:
    cur.execute("select * from MyTable")
    for column in cur.description:
        print(column)

 

1.2.3.2 可滾動光標

通過scrollable=True在創建光標時設置參數來創建可滾動光標。

cursor = connection.cursor(scrollable=True)
cursor.execute("select * from ChildTable order by ChildId")
​
cursor.scroll(mode="last")
print("LAST ROW:", cursor.fetchone())
​
cursor.scroll(mode="first")
print("FIRST ROW:", cursor.fetchone())
​
cursor.scroll(8, mode="absolute")
print("ROW 8:", cursor.fetchone())
​
cursor.scroll(6)
print("SKIP 6 ROWS:", cursor.fetchone())
​
cursor.scroll(-4)
print("SKIP BACK 4 ROWS:", cursor.fetchone())

 

1.2.3.3 限制行

查詢數據通常分為一組或多組:

  1. 給出查詢必須處理的行數的上限,這有助於提高數據庫的可伸縮性。

  2. 執行“網頁分頁”,允許根據需要從一組行移動到下一組或上一組。

  3. 用於獲取連續小集合中的所有數據以進行批處理,這個可通過調用fecthmany來處理

而其他的方法都要依托於SQL語句來實現。在文檔中有提到過"Oracle Database 12c SQL 引入了一個OFFSET/FETCH子句,它類似於LIMITMySQL的關鍵字。",只是這個我並沒有使用過。

一般來說,使用的都是ROWNUMMAX_NUMMIN_NUMROW_NUMBER()來進行限制。

需要注意的一點在於,ROWNUM似乎是與查詢結果綁定而不是表中結構綁定,譬如說,分頁取結果,若是使用下面的方法,是錯誤的。

select * from my_table where rownum<=10; --取前10
select * from my_table where rownum>10 and rownum<=20; --並不能取到10-20,,結果為空

 

使用ROWNUM進行行數限制的時候,需要先綁定變量。

select * from (select t.*,rownum as rnum from my_table  t) where rnum >10 and rnum <=20;

 

1.2.3.4 數據更新插入

可以使用cx_Oracle輕松執行SQL數據操作語言語句(DML),例如INSERT和UPDATE。

with  connection.cursor() as cur:
        cur.execute("insert into MyTable values (:idbv, :nmbv)", [1, "Fredico"])

 

 

  1. 不要將用戶數據連接或插入到 SQL 語句中。請參閱 使用綁定變量

  2. 有關提交和回滾數據更改的最佳實踐,請參閱事務管理

  3. 處理多個數據值時,executemany()用於性能。請參閱批處理語句執行和批量加載

當運行SQL語句時涉及到參數傳遞時,可以直接使用python拼接字符串,只是並不推薦。

cur.execute("insert into MyTable values ({}, {})".format(1, "Fredico"))

 

一般使用綁定變量,防止SQL注入。

cur.execute("insert into MyTable values (:idbv, :nmbv)", [1, "Fredico"])

 

而數據更新及插入時的事務處理,則是與方法Connection.commit()Connection.rollback()相關。

  1. 自動提交connection.autocommit在合適的時候設置為True,則不需要自己調用提交和回滾。

  2. 當存在多條語句操作時可在結束后再進行提交。

  3. 當數據庫連接關閉時,或者當引用連接的變量超出范圍時,任何未提交的事務都將被回滾,

1.2.3.5 批處理語句執行
data = [
    (10, 'Parent 10'),
    (20, 'Parent 20'),
    (30, 'Parent 30'),
    (40, 'Parent 40'),
    (50, 'Parent 50')
]
cursor.executemany("insert into ParentTable values (:1, :2)", data)

 

處理數據錯誤,輸出報錯的語句。

data = [
    (60, 'Parent 60'),
    (70, 'Parent 70'),
    (70, 'Parent 70 (duplicate)'),
    (80, 'Parent 80'),
    (80, 'Parent 80 (duplicate)'),
    (90, 'Parent 90')
]
cursor.executemany("insert into ParentTable values (:1, :2)", data,
                   batcherrors=True)
for error in cursor.getbatcherrors():
    print("Error", error.message, "at row offset", error.offset)
    
'''
Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 2
Error ORA-00001: unique constraint (PYTHONDEMO.PARENTTABLE_PK) violated at row offset 4
 
更多

 

一些類型、精度等需要的話,更加詳細的可看:SQL執行Oracle數據庫類型和cx_Oracle數據庫類型對照

調用function及存儲過程:PL/SQL執行

 

1.3 數據庫連接池

由於維護數據庫連接是一筆很大的開銷,當線程需要頻繁地創建連接、關閉連接時,使用數據庫連接池是一個很不錯的選擇。而且數據庫連接池還可以統一控制同一時間最大連接數。

cx_Oracle.SessionPool()提供了創建數據庫連接池的方法。

# 創建數據庫連接池
pool = cx_Oracle.SessionPool(user="hr", password=userpwd,
                             dsn="dbhost.example.com/orclpdb1", min=2,
                             max=5, increment=1, encoding="UTF-8")
​
# 請求池中的連接
connection = pool.acquire()
​
# 使用連接
cursor = connection.cursor()
for result in cursor.execute("select * from mytab"):
    print(result)
​
# 將連接返回池中
pool.release(connection)
​
# 關閉數據庫連接池
pool.close()

 

 

 
 

2、代碼

#!/usr/bin/pyhon
#coding=utf-8
​
​
import cx_Oracle
import datetime
​
class Oracle_util:
    
    url = 'ip:端口/服務名'
    user_name = '賬號名'
    psw = '密碼'# 構造函數
    def __init__(self):
        self.db = cx_Oracle.connect(user_name,psw,url)
        self.cursor = self.db.cursor()
    # close方法
    def close(self):
        self.cursor.close()
        self.db.close()
    # 查詢數據              
    def query(self,name):
        self.cursor.execute('select * from my_table where name=:name',name=name)
        
    # 獲取全部查詢結果
    def get_all(self):      
        return self.cursor.fetchall()
    # 逐一獲取查詢結果,返回的是元組   
    def get_one(self):  
        return self.cursor.fetchone()
    # 數據插入  
    def insert(self,rows):
        if 'id' not in rows.keys() or 'name' not in rows.keys() or 'time' not in rows.keys():
            print('數據插入錯誤:傳入數據格式錯誤。')
            return
         
        cur = self.db.cursor()
        cur.execute('insert into my_table(id,name,time) values (:id,:name,:time',rows)
        cur.close()
        self.db.commit() # 更新語句需要
    
    # 數據更新
    def update(self,rows):  
        if 'id' not in rows.keys() or 'name' not in rows.keys():
            print('數據更新錯誤:傳入數據格式錯誤。')
            return
    
        cur = self.db.cursor()
        cur.execute('update my_table set name=:name where id=:id',rows)
        cur.close()
        self.db.commit() # 更新語句需要
if __name__=='__main__':
    Oracle = Oracle_util()
    Oracle.query()
    c = 0
    while(1):
        rs = Oracle.get_one()
        c = c+1
        print(c)
        if(rs==None): break
        Oracle.insert(c,rs[1],datetime.datetime.now().strftime('%Y%m%d'))
        
    Oracle.update(2,'事實上')
    Oracle.close()
​

 


3、定時任務設置

windows配置定時任務,選擇.bat文件+schtasks的形式配置。

設置.bat文件運行主.py文件,如下

G: ::文件所在盤符
cd G:\文件\python ::文件位置
start python Oracleconnection.py >>log.txt
exit

 

設置schtasks每日定時任務如下(每日9:26運行,2021年12月31日截止),任務名用於查找任務,若已有同名任務存在可選擇覆蓋。

schtasks /create /tn 任務名 /tr 路徑\running.bat /sc daily /st 09:16:00 /ed 2021/12/31

 

查看已設置的定時任務。

schtasks /query /tn 任務名

 

結束任務:

schtasks /end /tn 任務名

 

刪除任務:

schtasks /delete /tn 任務名 /f

 

注意:可能運行失敗的原因

schtasks創建了,但是到了時間log.txt未寫入日志,即未正常運行python文件,原因:

  1. 路徑錯誤

  2. 中文亂碼問題

可在你創建schtasks的命令行目錄下運行:路徑\running.bat,根據報錯更改。

參考

python操作oracle數據庫

如何配置windows定時任務

BAT文件語法和技巧(bat文件的編寫及使用)

Windows 配置定時任務 和任務無法執行原因分析

Python安裝cx_Oracle與操作數據測試小結

 

 


免責聲明!

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



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