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.pswp.cn/news/278978.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/278978.shtml
英文地址,請注明出處:http://en.pswp.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文檔是將其個…

linux 內核fpic,linux編譯動態庫 fPIC作用

標簽:在生成動態庫時,常常習慣性的加上fPIC選項,fPIC有什么作用和意義,加不加有什么區別,這里做下小結:fPIC的全稱是 Position Independent Code, 用于生成位置無關代碼。什么是位置無關代碼&am…

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…

spring-cloud:利用eureka實現服務提供與調用示例

1.運行環境 開發工具:intellij idea JDK版本:1.8 項目管理工具:Maven 4.0.0 2.GITHUB地址 https://github.com/nbfujx/springCloud-learn-demo/tree/master/spring-cloud-eureka-producer-consumer

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%的碼農都做不了架構師?>>> 摘要: 如今,高性能計算已在不知不覺中滲透到人類生活的方方面面。從材料科學到石油勘探、金融市場、應急管理、醫學和互聯網等領域,高性能計算無一不大顯身手,幫助快速…

linux導出硬件信息,Linux 上生成硬件信息與配置報告: Sysreport

當需要 Linux 主機詳細完整的描述給另一個人的時候,這個工具非常適合。用 root 用戶之行,但要注意最好不要抓取 currently installed packages 信息,這一步非常慢。(用 -norpm 參數)存儲廠商 EMC 也有個 EMCgrab 工具,工作原理類似…

深度優先搜索(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 早上心態很好的繼續刷…

Linux決心書/李世超

Linux決心書大家好,我叫李世超,來自河北邯鄲。今年24,感覺之前的生活狀態不是自己想要的,每天渾渾噩噩。我覺得人要對自己定一個目標,我的目標就是月薪10K以上,所以我要努力在老男孩教育學習技術。珍惜這五…

linux下設備或資源忙,linux刪除文件目錄 目錄設備或資源忙怎么辦

linux刪除文件目錄 目錄設備或資源忙怎么辦來源:未知作者:老黑時間:09-09-21【打印】[rootrs swms]# rmdir zpggrmdir: ‘zpgg’: 設備或資源忙相關服務都已經停止掉了,有什么辦法強制刪除嗎?你可以在有windows的硬盤上…

Codeforces 1066 C(思維)

傳送門: 題面: C. Books Queries time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You have got a shelf and want to put some books on it. You are given qq queries of three type…

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中的一個奇怪問…