2012-04-13 55 views
5

我有一個SQL文件,我想在oracle中使用cx_Oracle python庫進行解析和執行。 SQL文件包含經典的DML/DDL和PL/SQL,例如。它可以是這樣的:使用Python中的cx_Oracle解析PL/SQL和DML/DDL的SQL文件

create.sql

-- This is some ; malicious comment 
CREATE TABLE FOO(id numeric); 

BEGIN 
    INSERT INTO FOO VALUES(1); 
    INSERT INTO FOO VALUES(2); 
    INSERT INTO FOO VALUES(3); 
END; 
/
CREATE TABLE BAR(id numeric); 

如果我使用的SQLDeveloper或SQL * Plus這個文件,它會被分成3個查詢和執行。但是,cx_Oracle.connect(...)。cursor()。execute(...)一次只能接受一個查詢,而不是整個文件。我不能簡單地使用string.split(';')(如這裏建議的execute a sql script file from cx_oracle?)拆分字符串,因爲這兩個註釋都將被拆分(並且會導致錯誤),並且PL/SQL塊將不會作爲單個命令執行,從而導致錯誤。

在Oracle論壇(https://forums.oracle.com/forums/thread.jspa?threadID=841025)上我發現cx_Oracle本身並不支持解析整個文件這樣的東西。我的問題是 - 有沒有工具可以幫我做這件事?例如。我可以調用將我的文件分割成查詢的python庫?

編輯:最好的解決方案似乎直接使用SQL * Plus。我已經使用這段代碼:

# open the file 
f = open(file_path, 'r') 
data = f.read() 
f.close() 

# add EXIT at the end so that SQL*Plus ends (there is no --no-interactive :(
data = "%s\n\nEXIT" % data 

# write result to a temp file (required, SQL*Plus takes a file name argument) 
f = open('tmp.file', 'w') 
f.write(data) 
f.close() 

# execute SQL*Plus 
output = subprocess.check_output(['sqlplus', '%s/%[email protected]%s' % (db_user, db_password, db_address), '@', 'tmp.file']) 

# if an error was found in the result, raise an Exception 
if output.find('ERROR at line') != -1: 
    raise Exception('%s\n\nStack:%s' % ('ERROR found in SQLPlus result', output)) 
+0

同樣的問題。基本上,Oracle是braindead,實際上沒有任何內置的能力來解析多語句SQL腳本,所以SQL * Plus和SQL Developer和TOAD都實現了它們自己的解析器:-( – 2016-01-08 03:28:41

回答

2

它可以同時執行多個語句,但它是半hacky。你需要包裝你的陳述並一次執行一個陳述。

>>> import cx_Oracle 
>>> 
>>> a = cx_Oracle.connect('schema/[email protected]') 
>>> curs = a.cursor() 
>>> SQL = (("""create table tmp_test (a date)"""), 
... ("""insert into tmp_test values (sysdate)""") 
...) 
>>> for i in SQL: 
...  print i 
... 
create table tmp_test (a date) 
insert into tmp_test values (sysdate) 
>>> for i in SQL: 
...  curs.execute(i) 
... 
>>> a.commit() 
>>> 

正如您已經注意到的,這並不能解決分號問題,因爲沒有簡單的答案。當我看到它,你有3種選擇:

  1. 寫的過於複雜的解析器,我不認爲這是一個不錯的選擇的。

  2. 不要從Python執行SQL腳本;將代碼放在單獨的SQL腳本中,這樣解析起來很簡單,在單獨的Python文件中,嵌入在Python代碼中,在數據庫中的過程中......等等。這可能是我的首選選項。

  3. 使用subprocess並以此方式調用腳本。這是最簡單和最快捷的選項,但根本不使用cx_Oracle

    >>> import subprocess 
    >>> cmdline = ['sqlplus','schema/[email protected]','@','tmp_test.sql'] 
    >>> subprocess.call(cmdline) 
    
    SQL*Plus: Release 9.2.0.1.0 - Production on Fri Apr 13 09:40:41 2012 
    
    Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved. 
    
    
    Connected to: 
    Oracle Database 11g Release 11.2.0.1.0 - 64bit Production 
    
    SQL> 
    SQL> CREATE TABLE FOO(id number); 
    
    Table created. 
    
    SQL> 
    SQL> BEGIN 
        2 INSERT INTO FOO VALUES(1); 
        3 INSERT INTO FOO VALUES(2); 
        4 INSERT INTO FOO VALUES(3); 
        5 END; 
        6/
    
    PL/SQL procedure successfully completed. 
    
    SQL> CREATE TABLE BAR(id number); 
    
    Table created. 
    
    SQL> 
    SQL> quit 
    Disconnected from Oracle Database 11g Release 11.2.0.1.0 - 64bit Production 
    0 
    >>> 
    
這裏
+0

嗨,子進程.call似乎是一個合理的解決方案(我無法使用第二個選項,因爲我無法控制SQL文件)。但是,當我將sqlplus作爲子進程運行時,它將等待「QUIT」命令。我的Python腳本需要非交互式(將有幾個SQL文件以這種方式執行),你將如何改變它,所以它不會等待stdin? – 2012-04-13 11:04:50

+0

@Savannah,你不能將退出添加到SQL腳本中嗎?確保它們退出 – Ben 2012-04-13 11:34:57

+0

我已將類似data =「%s \ n \ nEXIT」%data的數據添加到我運行的腳本中,並且它現在可以工作 – 2012-04-13 13:18:37