VB Oracle
VB Oracle
There is a great difference between the way we handle the PL/SQL table in ADO and OO4O. In ADO we use a Command object and a Recordset. With OO4O we use an OraSQLStmt object and a parameter array.
mCnn As ADODB.Connection mrsSelect As ADODB.Recordset mCmd As ADODB.Command msSelect As String mCmdPrmGender As New ADODB.Parameter
We must call our stored procedure using placeholder syntax and the call statement. The general form is: {call PackageName.ProcedureName(?, {resultset , Table1, Table2 )})} This will allow us to combine PL/SQL tables into a single Recordset. You may add as many parameters or tables as you need. Remember because the table is an output parameter, you may not be able to use more than 2 in early versions of Oracle. This limitation does not exist with OO4O. In our case: msSelect = "{call human_resources.first_names(?, {resultset 1000, fname})}" We want to get the whole PL/SQL table back into the mrsSelect Recordset (result set). The pair (in which the comma seems to be missing and is not) mrsSelect 1000 contains an arbitrary large number. The way the msSelect is coded with the parentheses is the syntax used for SQL Passthrough in ODBC. This limits the approach to using the ODBC provider. As we have said many times before, the ODBC provider is the most stable and the most able, but it is also the slowest. Here we did not have a choice. In ADO 2.5 the story is different, as you will see when we discuss REF CURSORS further along this chapter. We now set the Command object: Set mCmd = New ADODB.Command With mCmd .CommandText = msSelect .CommandType = adCmdText .ActiveConnection = mCnn Set mCmdPrmGender = .CreateParameter("pGender", adVarChar, _ adParamInput, 1, "M") .Parameters.Append mCmdPrmGender End With And create the Recordset, set the parameter value, assign the Recordset to the return value from the execution of the Command object. We now have the first names of men in the recordset and can have a look at the first field value: Set mrsSelect = New ADODB.Recordset mCmdPrmGender = "M" Set mrsSelect = mCmd.Execute MsgBox mrsSelect.Fields(0) End Sub
Private mMyQuery As OraSqlStmt Private Sub Form_Load() Set mOraSession = CreateObject("OracleInProcServer.XOraSession") ' Open the databae. Option 1 is ORA_DEFAULT Set mOraDatabase = mOraSession.OpenDatabase("", "scott/tiger", 1) ' Define the INPUT parameter and add it to the collecttion. mOraDatabase.Parameters.Add "pGender", "F", 1 ' Define the OUTPUT parameter array(defined as type VARCHAR2 by ' third argument) and addit to the collection. mOraDatabase.Parameters.AddTable "pfnames", 2, 1, 100, 15 ' make the SQL for calling the procedure. msSelect = "Begin Human_resources.first_names(:pGender, :pfnames); END;" ' Execute the SQL statement and create an OraSqlStmt object from the ' specified SQL statement and options. Option 0 is ORASQL_NO_AUTOBIND Set mMyQuery = mOraDatabase.CreateSql(msSelect, 0) ' Set the parameter value and refresh mOraDatabase.Parameters("pGender").Value = "F" mMyQuery.Refresh ' Look at the first record. MsgBox mOraDatabase.Parameters("pfnames").get_Value(0) End Sub
We then declare a Stored Procedure within the package, which has two parameters, the first of the two is fairly straightforward - pGender is an input parameter of type VARCHAR2. The second parameter (PeopleCursor) to this procedure is an IN OUT parameter, declared as type PeopleCur, which is the TYPE we have just defined. This tells us that the third parameter will be returning a REF CURSOR with structure of c1%rowtype. Now let's look at how we generate the package body, where the work of the procedure actually takes place. Note that there is no reference in this code to the cursor definition, as it only needs be defined once. All there is in the body is the PL/SQL code for the procedure. CREATE OR REPLACE PACKAGE BODY Personality AS PROCEDURE GetNames ( pGender IN VARCHAR2, PeopleCursor in out Peoplecur ) IS begin OPEN PeopleCursor FOR SELECT First_Name, Last_Name FROM People WHERE gender = pGender; END GetNames; END Personality; In the package body we actually build the stored procedure. The procedure takes in a pGender parameter. It then opens the PeopleCursor cursor and uses this parameter (specified in our WHERE clause) to get a cursor containing all the records in the People table that have field values corresponding to the value we give pGender. That's it. Again you may do the work in the Navigator or use the CREATE OR REPLACE approach in SQL*PLUS. Here is the latter:
When we get ADO 2.5 we'll be able to address the ref cursor directly as a recordset. This will eliminate the need to use the PL/SQL table mechanism that we have described before. OO4O already has this capability and the table example only served us to learn another way to achieve the same end. Given a choice, the ref cursor approach is the one I prefer.
SQL - The SQL statement must be a PL/SQL stored procedure with BEGIN and END around the call. CursorName should exactly match the cursor created inside the stored procedure Options - A long integer that assumes any combination (logical sum) of the following: Here is a table of valid Option values: Value 0 Constant ORADYN_DEFAULT Description In a read-only dynaset it means only that you get an Automatic MoveFirst (the dynaset when refreshed will already be at the first row), that blanks are stripped from the tail of a string, and that the dynaset caches as much as it can in the client memory.
2 8
64
ORADYN_NO_BLANKSTRIP The dynaset normally strips trailing blanks from character fields. This option leaves the trailing blanks in. ORADYN_NOCACHE With this option, because only one record is held in memory, you can do only forward movement (MoveNext, but no MovePrevious), but you get faster results. (*) ORADYN_NO_MOVEFIRST The dynaset is unpopulated. You have to do a MoveFirst to fill it.
(*) You get faster results most of the time. This is because we (the human user) usually want just one record at a time and takes his time before requesting the next record. On the other hand, if we want to do a batch process, this will make too many round trips. In such a case it is best to use a custom dynaset (see Chapter 8) and cache as many records as we can in one round trip. Armed with this knowledge let us proceed. Declarations: Private mDynGetNames As OraDynaset Private sSQL As String Establish our connection Set mOraSession = CreateObject("OracleInProcServer.XOraSession") Set mOraDatabase = mOraSession.DbOpenDatabase("", "scott/tiger", 1) Note the difference between this sSQL and the simpler ADO string.
' make the SQL for calling the procedure. sSQL = "Begin Personality.GetNames (:pGender,:PeopleCursor); end;" Note the exact spelling of PeopleCursor to match the name in the stored procedure. We have to declare the parameters before we can create the dynaset. As was the case with ADO, we do not declare a parameter for the cursor. This was taken care of by the correct spelling of the cursor name. mOraDatabase.Parameters.Add "pGender", "M", 1 'ORAPARM_INPUT Set mDynGetNames = mOraDatabase.CreatePlsqlDynaset(sSQL, _ "PeopleCursor", ORADYN_DEFAULT) Now execute by calling the Refresh method and have a look at the first field value: mDynGetNames.Refresh MsgBox mDynGetNames.Fields(0) Change the gender to F mOraDatabase.Parameters("pGender").Value = "F" And refresh again to get the ladies (I wish it were that simple!) mdynGetNames.Refresh MsgBox mDynGetNames.Fields(0) End Sub