Using Python with Oracle

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

Using Python with Oracle

This page discusses using Python with Oracle.

The page is based on the cx_oracle Python extension module. It was developed on a VM running Oracle Enterprise Linux 6U4 runnng Oracle 11.2.0.4 and Python 2.6.6.

Installation and Configuration

Assuming Python 2.6 has already been installed, the first job is to install the cx_oracle module. At the time of writing the current version was still 5.1.3 for which binaries are available for both Windows and Linux. For other platforms the sources can be downloaded and compiled.

I downloaded version 5.1.2 from http://sourceforge.net/projects/cx-oracle/files where the following RPM is available. This was the closest match to my environment

cx_Oracle-5.1.2-11g-py26-1.x86_64.rpm

Installation is standard. As the root user:

rpm -ivh cx_Oracle-5.1.2-11g-py26-1.x86_64.rpm

My VM already contained Oracle 11.2.0.4 Enterprise Edition with a database called "TARGET". I set the following environment variables in .bash_profile

export ORACLE_HOME=/u01/app/oracle/product/11.2.0/db_1
export LD_LIBRARY_PATH=$ORACLE_HOME/lib
export ORACLE_SID=TARGET

I rarely need to set $LD_LIBRARY_PATH, but in this case it was necessary to avoid raising the following error when the cx_Oracle is imported:

ImportError: libclntsh.so.11.1: cannot open shared object file: No such file or directory

Preparation

cx_oracle supports various connection methods. If possible I like to use a TNS alias with the Oracle client as the alias can be configured to use connection load balancing, failover etc. For this post my VM was called vm16, so the entry in $ORACLE_HOME/network/admin/tnsnames.ora was:

TARGET=
(DESCRIPTION=(ADDRESS=(HOST=vm16)(PROTOCOL=TCP)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=TARGET))
)

Example Script

The following is a complete script that connects to the database, executes a simple SELECT aggregate statement and returns the result

#!/usr/bin/python
# Example of fetchoneimport sys
import cx_Oracledef printf (format,*args):sys.stdout.write (format % args)def printException (exception):error, = exception.argsprintf ("Error code = %s\n",error.code);printf ("Error message = %s\n",error.message);username = 'scott'
password = 'tiger'
databaseName = "TARGET"try:connection = cx_Oracle.connect (username,password,databaseName)
except cx_Oracle.DatabaseError, exception:printf ('Failed to connect to %s\n',databaseName)printException (exception)exit (1)cursor = connection.cursor ()try:cursor.execute ('SELECT COUNT(*) FROM emp')
except cx_Oracle.DatabaseError, exception:printf ('Failed to select from EMP\n')printException (exception)exit (1)count = cursor.fetchone ()[0]
printf ('Count = %d\n',count)cursor.close ()connection.close ()exit (0)

The above code is discussed in the following sections

printf statement

Python does not include a printf statement. However the following library function provides similar functionality.

import sysdef printf (format,*args):sys.stdout.write (format % args)

This can be called using the familiar C syntax. For example:

printf ("Str %s Int %d\n",s,i)

printException function

I have also created a function to print details of any exceptions raised when accessing the database:

def printException (exception):error, = exception.argsprintf ("Error code = %s\n",error.code);printf ("Error message = %s\n",error.message);

Connection

The connection requires a username, password and TNS alias:

username = 'scott'
password = 'tiger'
databaseName = "TARGET"

Experience has demonstrated that it is good practice to execute cx_oracle methods that access the database within a try..except structure in order to catch and report any exceptions that they might throw. The printException function prints more detail

For the connection I have used the following code:

try:connection = cx_Oracle.connect (username,password,databaseName)
except cx_Oracle.DatabaseError, exception:printf ('Failed to connect to %s\n',databaseName)printException (exception)exit (1)

The connection should be closed when no longer required using:

connection.close ()

It is a good idea to develop the exception handling code at the outset, then most can be reused for other cx_oracle method calls. Remember to rollback any uncommitted transactions, close cursors and close any connections when the exception is raised.

Alternatively consider using a finally clause in the try statement. The finally clause of a try statement is executed irrespective of any exceptions that may be raised.

Cursor Management

In the previous section we created a connection. We now need to create a cursor as follows:

cursor = connection.cursor ()

The cursor should be closed when no longer required using:

cursor.close ()

The Cursor class has a number of powerful methods that allow statements and PL/SQL blocks to be prepared and executed

Execute method

The cursor.execute method is the easiest way to execute simple statements including DDL.

The simple SELECT statement is an aggregate returning a single column in a single row:

try:cursor.execute ('SELECT COUNT(*) FROM emp')
except cx_Oracle.DatabaseError, exception:printf ('Failed to select from EMP\n')printException (exception)exit (1)

We can check the result as follows:

count = cursor.fetchone ()[0]
printf ('Count = %d\n',count)

In this case the fetchone method is more appropriate when we are expecting a single row.

Alternatively we could have used the Cursor.fetchall method which fetches all rows returned by the statement:

count = cursor.fetchall ()[0][0]
printf ("Count = %d\n",count)

SELECT Statements

This section examines more complex examples of the SELECT statement. The next example returns multiple rows and columns:

sql = """\\SELECT empno,ename,sal FROM empWHERE sal > 2000ORDER BY sal DESC"""try:cursor.execute (sql)
except cx_Oracle.DatabaseError, exception:printf ('Failed to select from EMP\n')printException (exception)result = cursor.fetchall ()
for row in result:printf (" %s;%s;%d\n",row[0],row[1],row[2])

I have separated the SQL statement from the call to cursor.execute to improve readability. The triple quotes at the start and end of the SQL statement allow it to be written across multiple lines. This is particularly important for longer SQL statements where indentation is necessary. The backslash after the opening triple quote suppresses a newline character from being appended before the first line. A newline character will be appended to the remaining lines until the closing triple quote.

The results are returned using the Cursor.fetchall�method which returns a 2-dimensional array. All results are returned by the single call to fetchall and are stored in process memory. Obviously this works when there are only 14 employees - it would not be appropriate for larger result sets. In this case I am iterating through the results, printing them one line at a time. This works but is not particularly readable.

An alternative version of the code to handle the call to cursor.fetchall() follows:

for empno, ename, sal in cursor.fetchall ():printf (" %s;%s;%d\n",empno, ename, sal)

This version is more readable and requires less process memory

Bind Variables

So far our statements have only used literal values. These are not particularly scalable in high volume workloads so most applications use bind variables to reduce the parsing overhead.

The following example shows a statement with a couple of bind variables in the predicates

sql = """\\SELECT empno,ename,sal FROM empWHERE job = :jobAND deptno = :deptORDER BY sal DESC"""try:cursor.execute (sql,job = 'SALESMAN',dept = 30)
except cx_Oracle.DatabaseError, exception:printf ('Failed to select from EMP\n')printException (exception)for empno, ename, sal in cursor.fetchall ():printf (" %s;%s;%d\n",empno, ename, sal)

In the above example the call to cursor.execute includes the sql statement and values for the bind variables 'job' and 'dept'. Note from the second bind variable that the bind variable name does not have to be the same as the column value.

Prepare Statements

Whilst bind variables reduce parsing to a limited extent, they cannot eliminate it completely. To minimize parsing it is best to assign a prepared statement to a dedicated cursor. It is not then necessary to parse the statement again until the cursor is closed.

In the following code, the bind variable example from the previous section has been modified to use a separate prepare call.

sql = """\\SELECT empno,ename,sal FROM empWHERE job = :jobAND deptno = :deptORDER BY sal DESC"""try:cursor.prepare (sql)
except cx_Oracle.DatabaseError, exception:printf ('Failed to prepare cursor')printException (exception)exit (1)try:cursor.execute (None,job = 'SALESMAN',dept = 30)
except cx_Oracle.DatabaseError, exception:printf ('Failed to execute cursor')printException (exception)exit (1)for empno, ename, sal in cursor.fetchall ():printf (" %s;%s;%d\n",empno, ename, sal)

In the above example the statement is prepared using the cursor.prepare call and is then executed by the cursor.execute call.

Note that the first parameter of the cursor.execute call is "None". This specifies that the cursor should use the existing prepared statement instead of parsing a new SQL statement.

Alternatively the cursor.execute statement can take cursor.statement as the first parameter. For example:

try:cursor.execute (cursor.statement,job = 'SALESMAN',dept = 30)
except cx_Oracle.DatabaseError, exception:printf ('Failed to execute cursor')printException (exception)exit (1)

Cursor.statement returns the last statement parsed by the cursor.

INSERT Statements

This section discusses INSERT statements. The first example uses bind variables to insert a new row into the DEPT table.

sql = "INSERT INTO dept (deptno, dname, loc) VALUES (:deptno, :dname, :loc)"try:cursor.execute (sql,deptno=50, dname='MARKETING', loc='LONDON')
except cx_Oracle.DatabaseError, exception:printf ('Failed to insert row')printException (exception)exit (1)cursor.close ()connection.commit ()

In this example the values are supplied as parameters for the cursor.execute() method.

Note the commit statement at the end of the transaction

The following example uses a dictionary instead of the parameter list:

sql = "INSERT INTO dept (deptno, dname, loc) VALUES (:deptno, :dname, :loc)"try:cursor.execute (sql,{ 'deptno':50, 'dname': 'MARKETING', 'loc': 'LONDON'})
except cx_Oracle.DatabaseError, exception:printf ('Failed to insert row')printException (exception)exit (1)

In the above example a dictionary is passed to the cursor.execute () method. The dictionary contains name-value pairs for the column values to be inserted into the new row.

The next example demonstrates the use of a prepared INSERT statement:

sql = "INSERT INTO dept (deptno, dname, loc) VALUES (:deptno, :dname, :loc)"try:cursor.prepare (sql)
except cx_Oracle.DatabaseError, exception:printf ('Failed to prepare cursor')printException (exception)exit (1)try:cursor.execute (None,deptno=50, dname='MARKETING', loc='LONDON')
except cx_Oracle.DatabaseError, exception:printf ('Failed to insert row')printException (exception)exit (1)

As with the SELECT statement example, the cursor.execute method can use cursor.statement() instead of None, as shown below

cursor.execute (cursor.statement,deptno=50, dname='MARKETING', loc='LONDON')

UPDATE Statement

This section discusses UPDATE statements. The first example uses bind variables.

sql = "UPDATE dept SET loc = :loc WHERE deptno = :deptno"try:cursor.execute (sql,deptno = 50,loc = 'LONDON')
except cx_Oracle.DatabaseError, exception:printf ('Failed to execute cursor\n')printException (exception)exit (1)

In the above example, bind variable values are specified in the arguments of the call to cursor.execute() The next example uses a prepare statement

sql = "UPDATE dept SET loc = :loc WHERE deptno = :deptno"try:cursor.prepare (sql)
except cx_Oracle.DatabaseError, exception:printf ('Failed to prepare cursor\n')printException (exception)exit (1)try:cursor.execute (None,deptno = 50,loc = 'LONDON')
except cx_Oracle.DatabaseError, exception:printf ('Failed to execute cursor\n')printException (exception)exit (1)

As with the INSERT statement example, the Cursor.execute method can use Cursor.statement instead of None, as shown below:

cursor.execute (cursor.statement,deptno = 50,loc = 'LONDON')

DELETE Statement

This section discusses DELETE statements. The following is an example of a delete that uses a bind variable:

sql = "DELETE FROM dept WHERE deptno = :deptno"try:cursor.prepare (sql)
except:printf ('Failed to prepare cursor\n')exit (1)try:cursor.execute (None,deptno = 50)
except cx_Oracle.DatabaseError, exception:printf ('Failed to execute cursor\n')printException (exception)exit (1)

As with previous examples, Cursor.execute can use Cursor.statement instead of None, as shown below:

cursor.execute (cursor.statement,deptno = 50)

Array Operations

So far we have concentrated on statements which only make a single visit to the database for each execution. However, if we want our application to scale, at some stage we will need to consider batch operations to reduce the number of round trips from the client to the server. In this section we will examine batching techniques for SELECT statements and also INSERT, UPDATE and DELETE statements.

SELECT statements

For SELECT statements we can control the number of statements retrieved for each fetch using Cursor.arraysize.

The following statement sets the array size to 4 and then queries the EMP table:

cursor = connection.cursor ()sql = """\\SELECT empno, ename, jobFROM empORDER BY ename"""# set array size to 4
cursor.arraysize = 4try:cursor.execute (sql)
except cx_Oracle.DatabaseError, exception:printf ('Failed to select from EMP\n')printException (exception)exit (1)while True:rows = cursor.fetchmany()if rows == []:break;printf ("Fetched %d rows\n", len(rows))for empno,ename,job in rows:printf (" %s;%s;%s\n",empno,ename,job)

The above example includes the creation of the cursor using connection.cursor() as the cursor must exist before the array size can be modified.

In this example we are using the Cursor.fetchmany method to fetch the rows. Every fetch with the exception of the last one will return four rows to the application.

The output of the above script is:

Fetched 4 rows7876;ADAMS;CLERK7499;ALLEN;SALESMAN7698;BLAKE;MANAGER7782;CLARK;MANAGER
Fetched 4 rows7902;FORD;ANALYST7900;JAMES;CLERK7566;JONES;MANAGER7839;KING;PRESIDENT
Fetched 4 rows7654;MARTIN;SALESMAN7934;MILLER;CLERK7788;SCOTT;ANALYST7369;SMITH;CLERK
Fetched 2 rows7844;TURNER;SALESMAN7521;WARD;SALESMAN

There are 14 rows in the EMP table.

The advantage of specifying an array size is that the client has more control over memory usage. Only the rows retrieved by one call to fetchmany, in this case four, will be stored in memory on the client. If we had used Cursor.fetchall instead of fetchmany, then all rows would be returned and would need to be stored in client memory.

The impact on the database server depends on the nature of the query. If the query was a set operation then all result rows would need to be stored in local memory on the database server until they had been fetched by the application or the cursor was closed. If the query was a row operation then it may only be necessary to store sufficient results to satisfy the latest fetch request.

As the above query includes an ORDER by clause, it is almost certainly executing as a set operation as there is not index on the ENAME column.

Setting the array size controls the number of network messages exchanged between the client and the server.

The DB API recommends that the default array size is 1. However, cx_oracle ignores this recommendation and sets the default to 50. We can check this using:

printf ("Array Size = %d\n",cursor.arraysize);

If cursor.arraysize is not set, then the query will fetch all rows. Output will be:

Fetched 14 rows7876;ADAMS;CLERK7499;ALLEN;SALESMAN7698;BLAKE;MANAGER7782;CLARK;MANAGER7902;FORD;ANALYST7900;JAMES;CLERK7566;JONES;MANAGER7839;KING;PRESIDENT7654;MARTIN;SALESMAN7934;MILLER;CLERK7788;SCOTT;ANALYST7369;SMITH;CLERK7844;TURNER;SALESMAN7521;WARD;SALESMAN

If cursor.arraysize is set to 1, then the query will fetch one row at a time. Output will be:

Fetched 1 rows7876;ADAMS;CLERK
Fetched 1 rows7499;ALLEN;SALESMAN
Fetched 1 rows7698;BLAKE;MANAGER
Fetched 1 rows7782;CLARK;MANAGER
Fetched 1 rows7902;FORD;ANALYST
Fetched 1 rows7900;JAMES;CLERK
Fetched 1 rows7566;JONES;MANAGER
Fetched 1 rows7839;KING;PRESIDENT
Fetched 1 rows7654;MARTIN;SALESMAN
Fetched 1 rows7934;MILLER;CLERK
Fetched 1 rows7788;SCOTT;ANALYST
Fetched 1 rows7369;SMITH;CLERK
Fetched 1 rows7844;TURNER;SALESMAN
Fetched 1 rows7521;WARD;SALESMAN

Array INSERT Statement

The previous example investigated use of array size to manage fetches from SELECT statements. In the following sections we will discuss use of arrays with INSERT, UPDATE and DELETE statements. In this section we will cover array INSERT statements.

The following is an example of an array INSERT of three rows into the DEPT table:

sql = "INSERT INTO dept (deptno, dname, loc) VALUES (:deptno, :dname, :loc)"try:cursor.prepare (sql)
except cx_Oracle.DatabaseError, exception:printf ('Failed to prepare cursor')printException (exception)exit (1)array=[]
array.append ((50,'MARKETING','LONDON'))
array.append ((60,'HR','PARIS'))
array.append ((70,'DEVELOPMENT','MADRID'))try:cursor.executemany (None,array)
except cx_Oracle.DatabaseError, exception:printf ('Failed to insert rows')printException (exception)exit (1)

Note that we are using the Cursor.executemany () method instead of Cursor.execute ()

If we enable trace for the above statement we can verify that an array insert was performed on the database server:

PARSING IN CURSOR #140238572911728 len=68 dep=0 uid=83 oct=2 lid=83 tim=1419870619434242 hv=3863288051 ad='91cf3288' sqlid='9fxqrt3m4a67m'
INSERT INTO dept (deptno, dname, loc) VALUES (:deptno, :dname, :loc)
END OF STMT
PARSE #140238572911728:c=0,e=267,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=0,tim=1419870619434241
EXEC #140238572911728:c=1000,e=516,p=0,cr=1,cu=7,mis=1,r=3,dep=0,og=1,plh=0,tim=1419870619434770
STAT #140238572911728 id=1 cnt=0 pid=0 pos=1 obj=0 op='LOAD TABLE CONVENTIONAL  (cr=1 pr=0 pw=0 time=258 us)'
WAIT #140238572911728: nam='SQL*Net message to client' ela= 17 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1419870619435236
WAIT #140238572911728: nam='SQL*Net message from client' ela= 174 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1419870619435451
CLOSE #140238572911728:c=0,e=5,dep=0,type=0,tim=1419870619435486
XCTEND rlbk=0, rd_only=0, tim=1419870619435523

In the above, the EXEC line includes r=3 which indicates that three rows were inserted.

We could also call executemany using cursor.statement as the first parameter:

cursor.executemany (cursor.statement,array)

Array UPDATE Statement

We can also use arrays with UPDATE statements. In the following example, we are using a dictionary to pass the bind variable values to the cursor.executemany method:

sql = "UPDATE dept SET loc = :loc WHERE deptno = :dept"try:cursor.prepare (sql)
except:printf ('Failed to prepare cursor')exit (1)array=[]
array.append ({'dept':50,'loc':'SHANGHAI'})
array.append ({'dept':60,'loc':'MOSCOW'})
array.append ({'dept':70,'loc':'MUMBAI'})try:cursor.executemany(None,array)
except cx_Oracle.DatabaseError, exception:printf ('Failed to execute cursor')printException (exception)exit (1)

Again the trace shows that all three rows were updated using a single execution of the UPDATE statement:

PARSING IN CURSOR #140267303715936 len=47 dep=0 uid=83 oct=6 lid=83 tim=1419873983403707 hv=2665682896 ad='8bda6038' sqlid='gja8j7agf65yh'
UPDATE dept SET loc = :loc WHERE deptno = :dept
END OF STMT
PARSE #140267303715936:c=0,e=450,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=267198286,tim=1419873983403669
EXEC #140267303715936:c=0,e=317,p=0,cr=3,cu=5,mis=0,r=3,dep=0,og=1,plh=267198286,tim=1419873983404140
STAT #140267303715936 id=1 cnt=0 pid=0 pos=1 obj=0 op='UPDATE  DEPT (cr=3 pr=0 pw=0 time=212 us)'
STAT #140267303715936 id=2 cnt=3 pid=1 pos=1 obj=87107 op='INDEX UNIQUE SCAN PK_DEPT (cr=3 pr=0 pw=0 time=70 us cost=1 size=21 card=1)'
WAIT #140267303715936: nam='SQL*Net message to client' ela= 1 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1419873983404265
WAIT #140267303715936: nam='SQL*Net message from client' ela= 133 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1419873983404418
CLOSE #140267303715936:c=0,e=4,dep=0,type=0,tim=1419873983404447
XCTEND rlbk=0, rd_only=0, tim=1419873983404492

As with the previous example, the EXEC line includes r=3 which indicates that three rows were updated.

Array DELETE Statement

Arrays can also be used with DELETE statements. As with UPDATE statements this example uses a dictionary to specify bind variables

sql = "DELETE FROM dept WHERE deptno = :dept"try:cursor.prepare (sql)
except:printf ('Failed to prepare cursor')exit (1)array=[]
array.append ({'dept':50})
array.append ({'dept':60})
array.append ({'dept':70})try:cursor.executemany(None,array)
except cx_Oracle.DatabaseError, exception:printf ('Failed to execute cursor')printException (exception)exit (1)

In the above example, the array is used to specify the deptno for each of the three rows to be deleted

As in the previous DML examples, the EXEC line in the trace file includes r=3 indicating that three rows were deleted

PARSING IN CURSOR #140404237827184 len=37 dep=0 uid=83 oct=7 lid=83 tim=1419881960556726 hv=1258566750 ad='8be19350' sqlid='3mpppmd5h8d2y'
DELETE FROM dept WHERE deptno = :dept
END OF STMT
PARSE #140404237827184:c=0,e=234,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=0,tim=1419881960556725
EXEC #140404237827184:c=11999,e=12902,p=0,cr=49,cu=18,mis=1,r=3,dep=0,og=1,plh=2529563076,tim=1419881960569679
STAT #140404237827184 id=1 cnt=0 pid=0 pos=1 obj=0 op='DELETE  DEPT (cr=49 pr=0 pw=0 time=12065 us)'
STAT #140404237827184 id=2 cnt=3 pid=1 pos=1 obj=87107 op='INDEX UNIQUE SCAN PK_DEPT (cr=3 pr=0 pw=0 time=58 us cost=1 size=13 card=1)'
WAIT #140404237827184: nam='SQL*Net message to client' ela= 15 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1419881960570446
WAIT #140404237827184: nam='SQL*Net message from client' ela= 200 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1419881960570681
CLOSE #140404237827184:c=0,e=22,dep=0,type=0,tim=1419881960570758
XCTEND rlbk=0, rd_only=0, tim=1419881960570869

Calling PL/SQL Subroutines

PL/SQL procedures and functions can be called using the Cursor.callproc and Cursor.callfunc methods

PL/SQL Procedures

Assume we have created the following PL/SQL procedure

CREATE OR REPLACE PROCEDURE proc1
(p_deptno NUMBER,p_dname VARCHAR2,p_loc VARCHAR2
)
IS
BEGININSERT INTO dept (deptno, dname, loc) VALUES (p_deptno, p_dname, p_loc);
END;
/

We can call the procedure using the following code:

deptno = 50
dname = 'MARKETING'
loc   = 'LONDON'try:cursor.callproc('PROC1',(deptno,dname,loc))
except cx_Oracle.DatabaseError, exception:printf ('Failed to call procedure')printException (exception)exit (1)

The cursor.callproc method specifies the name of the procedure and also a list of bind variable parameters.

Note that in this example the bind variables are input parameters.

PL/SQL Functions

Assume we have created the following PL/SQL function which returns the number of employees with a specific job in a specific department:

CREATE OR REPLACE FUNCTION func1
(p_job VARCHAR2,p_deptno NUMBER
)
RETURN NUMBER
ISl_result NUMBER;
BEGINSELECT COUNT(*) INTO l_result FROM emp WHERE job = p_job AND deptno = p_deptno;RETURN l_result;
END;
/

To call the function we use the cursor.callfunc method

job = 'SALESMAN'
deptno = 30result = cursor.var (cx_Oracle.NUMBER)try:cursor.callfunc('FUNC1',result,(job,deptno))
except cx_Oracle.DatabaseError, exception:printf ('Failed to call function')printException (exception)exit (1)printf ("Result = %d\n",result.getvalue ())

The above code creates a result variable using the cursor.var method. It then calls the PL/SQL function FUNC1 passing the job and deptno as bind variables. The result is returned in a Variable object and is then converted to a number using the Variable.getvalue method.

Procedures with OUT parameters

If a PL/SQL subroutine only returns one value it will typically be written as a function. However if the PL/SQL subroutine needs to return more than one value it will usually return them as OUT parameters.

The following procedure returns the employee name and job for a specific employee number.

CREATE OR REPLACE PROCEDURE proc2
(p_empno NUMBER,p_ename OUT VARCHAR2,p_job OUT VARCHAR2
)
IS
BEGINSELECT ename,job INTO p_ename,p_job FROM emp WHERE empno = p_empno;
END;
/

The procedure has one input parameter (p_empno) and two output parameters (p_ename and p_job)

The following code calls PROC2:

empno = 7839
ename = cursor.var (cx_Oracle.STRING)
job   = cursor.var (cx_Oracle.STRING)try:cursor.callproc('PROC2',(empno,ename,job))
except cx_Oracle.DatabaseError, exception:printf ('Failed to call procedure\n')printException (exception)exit (1)printf ("Ename = %s\n",ename.getvalue ())
printf ("Job = %s\n",job.getvalue ())

The above code creates an input parameter for empno and two output parameters to received the results for ename and job. The procedure is called using the Cursor.callproc method specifying the name of the procedure (PROC2) and a list of parameters.

The OUT parameters are returned as Variable objects. The Variable.getvalue method must be called to extract the value from the object.

Procedures with IN-OUT parameters

It is debatable whether IN-OUT parameters for simple data types are ever justified and it is therefore difficult to devise a credible example for the scott/tiger database. However, it is often necessary to call subroutines developed by others in which case they may follow different standards.

The following PL/SQL procedure adds the specified values for SAL and COMM to the existing values in the table for the specified EMPNO. The PL/SQL procedure returns the new values in the same parameters.

CREATE OR REPLACE PROCEDURE proc3
(p_empno NUMBER,p_sal   IN OUT NUMBER,p_comm  IN OUT NUMBER
)
IS
BEGINUPDATE emp SET sal = sal + p_sal, comm = comm + p_commWHERE empno = p_empnoRETURNING sal, comm INTO p_sal,p_comm;
END;
/

The following code calls the PROC3 procedure for EMPNO 7499

empno = 7499sal = cursor.var (cx_Oracle.NUMBER)
sal.setvalue (0,400)comm = cursor.var (cx_Oracle.NUMBER)
comm.setvalue (0,200)try:cursor.callproc('PROC3',(empno,sal,comm))
except cx_Oracle.DatabaseError, exception:printf ('Failed to call procedure')printException (exception)exit (1)printf ("sal = %d\n",sal.getvalue ())
printf ("comm = %d\n",comm.getvalue ())

The above code sets a value for EMPNO which is an input parameter. It then creates Variable objects for the two output parameters and sets initial values for these parameters using the Variable.setvalue method. The first parameter in the Cursor.var call will normally be 0.

The procedure then calls PROC3 using the Cursor.callproc method. When the procedure returns, the values of the SAL and COMM parameters are extracted from the Variable objects using the Variable.getvalue method

转载于:https://my.oschina.net/zhiyonghe/blog/1585674

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/278978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小米oj 反向位整数(简单位运算)

反向位整数 序号:#30难度:一般时间限制:1000ms内存限制:10M 描述 输入32位无符号整数,输出它的反向位。 例,输入4626149(以二进制表示为00000000010001101001011011100101)&#xff…

如何在Microsoft Word中插入签名

Adding your signature to a Microsoft Word document is the ultimate way to personalize it as your own, especially for documents like letters or contracts. If you want to add a signature to a Word document, here’s how. 将签名添加到Microsoft Word文档是将其个…

jdk 1结尾和2结尾_“与巢一起工作”的结尾对您意味着什么

jdk 1结尾和2结尾korisbo/Shutterstock科里斯博/ ShutterstockGoogle announced the end of “Works With Nest” at Google I/O 2019. Many companies, from IFTTT to Philips Hue, use Works With Nest to automate your smarthome. Those automations will break soon. Goog…

linux 桌面显示视频播放器,Ubuntu 13.10开启媒体播放器VLC桌面通知的步骤

VLC是一款多功能的媒体播放器,支持众多音频及视频格式,能够适用于Ubuntu等系统,而VLC播放器有桌面通知功能,需要手动开启,下面小编就以Ubuntu 13.10为例,给大家详细介绍下Ubuntu 13.10开启VLC桌面通知的步骤…

独家 | 蚂蚁金服TRaaS技术风险防控平台解密

小蚂蚁说: 在金融行业,风险防控能力的重要性不言而喻。而蚂蚁金服可实现高达99.999%的异地多活容灾,千亿级资金秒级实时核对“账、证、实”等能力也让业界有目共睹。 今年位于杭州的蚂蚁金服ATEC科技大会上,蚂蚁金服正式推出技术风…

谷歌表格_如何使用宏自动执行Google表格

谷歌表格Google Sheets lets you automate repetitive tasks with macros, and you can then bind them to keyboard shortcuts to execute them quickly. They work using Google Apps Script to capture your actions for later use. Google表格可以让您使用宏自动执行重复性…

阿里云弹性高性能计算E-HPC强势来袭,全新打造一站式云超算

为什么80%的码农都做不了架构师?>>> 摘要: 如今,高性能计算已在不知不觉中渗透到人类生活的方方面面。从材料科学到石油勘探、金融市场、应急管理、医学和互联网等领域,高性能计算无一不大显身手,帮助快速…

深度优先搜索(DFS)----------------Tju_Oj_3517The longest athletic track

这个题主要考察对树的操作,主要思想是DFS或者BFS,其次是找树的直径方法(既要运用两次BFS/DFS),最后作为小白,还练习了vector的操作。 DFS框架伪码: bool DSF(Node oneTreePoint ){ //传入的结点和其他有效…

word中图片超出页边距_如何在Word中更改页边距

word中图片超出页边距Word documents open with one-inch margins by default. You can adjust the page margins by choosing one of Word’s predefined options, or you can specify the exact height and width of the margins yourself. Here’s how. 默认情况下&#xff…

Android 中文 API (16) —— AnalogClock

一、结构 public class AnalogClock extends View java.lang.Object android.view.View android.widget.AnalogClock 二、类概述 这是一个带有时针和分针的模拟时钟控件。 三、受保护的方法 protected void onAttachedToWindow () 视图(AnalogClock)附在…

linux服务器探针软件,服务器安装ServerStatus监控探针教程

前言本文将介绍在服务器上安装ServerStatus来监控多台服务器的运行状态的教程.ServerStatus-Toyo版是一个酷炫高逼格的云探针、云监控、服务器云监控、多服务器探针~,该云监控(云探针)ServerStatus-Toyo项目链接本文为Stille原创文章.经实践,测试,整理发布.如需转载…

iphone播客怎么上传_如何在iPhone,iPad或Android上收听播客

iphone播客怎么上传Khamosh PathakKhamosh PathakDid someone recently recommend you listen to a podcast? If your response was, “What’s a podcast?” we’ve got the answer, and more! Here’s a crash course on podcasts and how you can listen to them on your …

NOIP2018 退役记

NOIP挂完,OI再见 AFO Day 0 早上的高铁,1点多到广州,2点多到酒店,下午就是颓颓颓,然后晚上随便刷了一下板子,反正PJ也没啥板子可以刷 就这样浪费了一天,我到底在干嘛 Day 1 早上心态很好的继续刷…

outlook默认签名设置_如何将默认签名添加到Outlook会议请求

outlook默认签名设置An odd quirk in Outlook is the inability to add a default signature to meeting requests. Here’s a quick and simple way to set up a one-click solution that avoids cutting and pasting every time you create a meeting. Outlook中的一个奇怪问…

技嘉 linux设置u盘启动项,技嘉主板bios设置u盘启动教程

对于想要重装系统的朋友来说,进bios一直是最大的难关,对于技嘉主板来说尤为复杂,下面小编就详细给大家介绍一下技嘉主板bios设置u盘启动的方法。方法一:使用u盘启动快捷键直接进入u盘装系统1、技嘉主板u盘启动快捷键是F12&#xf…

outlook日历不显示_如何在Outlook Online中突出显示不同的日历

outlook日历不显示If you’ve ever displayed multiple calendars in one view in Outlook Online, you’ll know how useful it is but also how confusing it can get. Use colors and charms to know at a glance which appointment belongs to which calendar. 如果您曾经在…

linux 下eclipse调试程序,文章2 Linux安装Eclipse阅读及调试程序

由于安装Eclipse需要Java环境,还需要配置环境,非常复杂,建议安装系统时,选择上Eclipse开发工具但是安装的Eclipse中没有CDT。首先给Eclipse安装一个CDT。1.安装CDTEclipse菜单栏help----Install New Software.从Available Softwar…

Redis学习笔记~分布式的Pub/Sub模式

redis的客户端有很多,这次用它的pub/sub发布与订阅我选择了StackExchange.Redis,发布与订阅大家应该很清楚了,首先一个订阅者,订阅一个服务,服务执行一些处理程序(可能是写个日志,插入个数据&am…

easyui关机图标_如何在Windows 10中创建关机图标

easyui关机图标It’s true that shutting down your Windows 10 PC the old-fashioned way only takes three clicks. But why spend the extra energy when you can do it in two? All you have to do is create a shutdown icon, and you’ll save yourself some time. 的确…

Struts2+JFreeChart

下面以边帖图片和代码的方式来讲解Struts2与JFreeChart的整合。搭建环境:首先帖一张工程的目录结构以及所需的jar包。注意:如果你不打算自己写ChartResult的话只需要引入struts2-jfreechart-plugin-2.0.6.jar(这个在struts-2.0.6-all.zip可以找到了): …