2013-08-16 4 views
5

Ich möchte eine Oracle PL/SQL-Anweisung über Cx_oracle in Python ausführen. Code sieht wie folgt aus:Rückgabevariable von cx_Oracle PL/SQL-Aufruf in Python

db = cx_Oracle.connect(user, pass, dsn_tns) 
cursor = db.cursor() 

... 

sel = """ 
DECLARE 
    c NUMBER := 0.2; 
    mn NUMBER := 1.5; 
    res NUMBER; 
BEGIN 
    res := c+mn/6.; 
END; 
""" 
try: 
    cursor.execute(sel) 
    print "PL/SQL successful executed ..." 
except cx_Oracle.DatabaseError as e: 
    err, = e.args 
    print "\n".join([str(err.code),err.message,err.context]) 

Der Code ohne Probleme läuft, aber es ist es eine Chance, das Ergebnis zurück zu Python zu bekommen?

Antwort

7

Sie benötigen eine Funktion, um ein Ergebnis zurückzugeben. Ein anonymer Block wird nicht.

Sie müssen create a function in der Datenbank, zum Beispiel:

create or replace function calculation return number is 
    c number := 0.2; 
    mn number := 1.5; 
    res number; 
begin 
    return c + mn/6.; 
end; 
/

Dann wird Ihr Python-Code ändern, um die Funktion aufzurufen, verwenden, callfunc()

db = cx_Oracle.connect(user, pass, dsn_tns) 
cursor = db.cursor() 

try: 
    result = cursor.callfunc('calculation', float) 
    print result 
except cx_Oracle.DatabaseError as e: 
    err, = e.args 
    print "\n".join([str(err.code),err.message,err.context]) 

Es ist nicht möglich zu erstellen eine Funktion im laufenden Betrieb, aber Ihre Funktion ist einfach genug, dass Sie es in einer Select-Anweisung tun können und fetchall() verwenden, wie in der verknüpften Dokumentation beschrieben um das Ergebnis an Python zurückzugeben. fetchall() gibt eine Liste von Tupeln zurück. Wenn Sie nur nach einer einzelnen Zeile und Spalte suchen, können Sie sofort den Index 0.th von beiden auswählen.

>>> import cx_Oracle 
>>> db = cx_Oracle.connect('****','****','****') 
>>> cursor = db.cursor() 
>>> SQL = """select 0.2 + 1.5/6. from dual""" 
>>> try: 
...  cursor.execute(SQL) 
...  result = cursor.fetchall()[0][0] 
... except cx_Oracle.DataBaseError, e: 
...  pass 
... 
<__builtin__.OracleCursor on <cx_Oracle.Connection to ****@****>> 
>>> result 
0.45000000000000001 
>>> 

Sie können auch die Variablen in Ihre execute() Anruf mit Bind-Variablen übergeben und damit sie in Python instanziiert, falls erforderlich:

>>> c = 0.2 
>>> mn = 1.5 
>>> SQL = """select :c + :mn/6. from dual""" 
>>> bind_vars = { 'c' : c, 'mn' : mn } 
>>> cursor.execute(SQL, bind_vars) 
<__builtin__.OracleCursor on <cx_Oracle.Connection to [email protected]>> 
>>> result = cursor.fetchall()[0][0] 
>>> result 
0.45000000000000001 
>>> 

Obwohl es vielleicht einfacher sein, all dies in Python zu tun ... Ich nehme an, Ihre tatsächliche Situation ist komplizierter?

+0

Ist es möglich, die Funktion vorübergehend „on the fly“ in einer Art und Weise zu erstellen drucken, dass die Datenbank vergessen db.close nach dem Aufruf von()? –

+0

Bitte beachten Sie mein Update @ user1946052. – Ben

+0

Hat die Tabelle "dual" irgendeine Bedeutung oder ist sie eine Art Platzhalter? –

8

Sie können Eingabe- und Ausgabevariablen wie folgt an den Block binden.

import cx_Oracle 

SQL_BLOCK = ''' 
DECLARE 
    v_first NUMBER; 
    v_second NUMBER; 
    v_result NUMBER; 
BEGIN 
    v_first := :i_first; -- (1) 
    v_second := :i_second; -- (1) 

    v_result := (v_first + v_second)/2; 

    :o_result := v_result; -- (1) 
END; 
''' 

with cx_Oracle.connect('hr/[email protected]') as db: 
    cur = db.cursor() 
    o_result = cur.var(cx_Oracle.NUMBER) # (2) 
    cur.execute(SQL_BLOCK, i_first=23, i_second=55, o_result=o_result) # (3) 
    res = o_result.getvalue() # (4) 
    print('Average of 23 and 55 is: {}'.format(res)) 
  1. Mit der regulären bind Notation (:) in dem PL/SQL-Block für beide Eingangs- und Ausgangsvariablen
  2. der Ausgangsvariablen eine Variable aus dem Cursor (des entsprechenden Typs erhalten)
  3. in die Ausführungs nennen Werte für die Eingangsgrößen vorzusehen und die Variable von (2) als Parameter
  4. den Wert aus den Ausgangsgrößen Retrieve

Das Skript sollte

Average of 23 and 55 is: 39.0