Get results of (Oracle) stored procedure output parameters in Access - oracle

I've never worked with Stored Procedures, so I'm completely lost. One of our DB admin created a SP in Oracle that accepts three variables, and has one OUT variable. How do I see the results of the procedure once I call it?
DECLARE
v_srch_sw VARCHAR2(1);
v_srch_crit VARCHAR2(20);
v_ssn_last_four VARCHAR2(4);
v_ivr_data VARCHAR2(4000);
BEGIN
DBMS_OUTPUT.enable(buffer_size => NULL);
v_srch_sw := 'A';
v_srch_crit := '1234567890';
v_ssn_last_four := '1234';
schemaname.sp_name_010(v_srch_sw
,v_srch_crit
,v_ssn_last_four
,v_ivr_data);
DBMS_OUTPUT.PUT_LINE(v_ivr_data);
END;
/
Note: The DBMS_OUTPUT.PUT_LINE returns the results as a message, but not an actual result. For example, when I put this code in MS Access '07 as a pass-through query, no results are returned. How do I get this to return the results just like normal SELECT query?

How do I get this to return the results just like normal SELECT query?
First, be aware that the code in the question is not the stored procedure, it is PL/SQL code to invoke the stored procedure and display the results in Oracle.
Second, AFAIK there is no way to invoke that stored procedure from Access "like a normal SELECT query" because the stored procedure does not return a result set. Instead, it returns its value as an output parameter.
However, you can create a Function in VBA that calls the stored procedure and returns the result. The following example works with SQL Server, but the code for Oracle should be very similar. (For example the parameter names might be slightly different, possibly omitting the # prefix.)
Option Compare Database
Option Explicit
Public Function Call_sp_name_010(v_srch_sw As String, v_srch_crit As String, v_ssn_last_four As String) As Variant
Const linkedTableName = "dbo_Clients" ' an existing ODBC linked table
Dim cdb As DAO.Database
Set cdb = CurrentDb
Dim con As New ADODB.Connection
con.Open "DSN=" & Mid(cdb.TableDefs(linkedTableName).Connect, 5) ' omit "ODBC;" prefix
Dim cmd As New ADODB.Command
cmd.ActiveConnection = con
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "schemaname.sp_name_010"
cmd.Parameters.Append cmd.CreateParameter("#v_srch_sw", adVarChar, adParamInput, 1)
cmd.Parameters.Append cmd.CreateParameter("#v_srch_crit", adVarChar, adParamInput, 20)
cmd.Parameters.Append cmd.CreateParameter("#v_ssn_last_four", adVarChar, adParamInput, 4)
cmd.Parameters.Append cmd.CreateParameter("#v_ivr_data", adVarChar, adParamOutput, 4000)
cmd.Parameters("#v_srch_sw").Value = v_srch_sw
cmd.Parameters("#v_srch_crit").Value = v_srch_crit
cmd.Parameters("#v_ssn_last_four").Value = v_ssn_last_four
cmd.Execute
' function returns output parameter value
Call_sp_name_010 = cmd.Parameters("#v_ivr_data").Value
Set cmd = Nothing
con.Close
Set con = Nothing
Set cmd = Nothing
End Function
Now, if necessary, you can use that user-defined function in an Access query.

Related

Calling an oracle stored procedure with no parameters ADODB

I have not found anywhere on how to call an Oracle stored procedure using ADODB from Excel where the stored procedure has no input parameters.
Fake example to illustrate lack of input parameters:
CREATE OR REPLACE PROCEDURE Get_Data
(OUTPUT OUT SYS_REFCURSOR) IS
******************************************************************************/
BEGIN
OPEN OUTPUT FOR
SELECT DISTINCT
B.ITEM_ID
B.ITEM_DESC
FROM ITEM_FILE B
WHERE ITEM_ID IS NOT NULL
ORDER BY B.ITEM_ID
;
END Get_Data;
/
Oh, and the stored procedure is required because we don't want to give users SQL access to create whatever SQL they want.
Is this even possible? And if so, what kind of code would it take to call it?
Thanks,
Dan
Transform your procedure to a function:
CREATE OR REPLACE FUNCTION Get_Data RETURN SYS_REFCURSOR IS
res SYS_REFCURSOR;
BEGIN
OPEN OUTPUT FOR
SELECT DISTINCT
B.ITEM_ID
B.ITEM_DESC
FROM ITEM_FILE B
WHERE ITEM_ID IS NOT NULL
ORDER BY B.ITEM_ID;
RETURN res;
END Get_Data;
/
Call in VBA would be as this:
cmd.CommandText = "{CALL Get_Data()}"
cmd.Properties("PLSQLRSet") = True
Set Rst1 = cmd.Execute
cmd.Properties("PLSQLRSet") = False
Note, by default an OUT parameters are used like this:
cmd.Parameters.Append cmd.CreateParameter("OUTPUT", adVarChar, adParamOutput, 100)
However, for RefCursor parameters you must not declare them with cmd.Parameters.Append.
Have a look at Provider for OLE DB Developer's Guide, it contains several examples. Chapter Stored Procedures and Functions Returning Rowsets should be the most relevant for you.

Oracle 12c: Functions in the WITH Clause do not work with ADODB

select banner
from v$version
;
BANNER
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
"CORE 12.1.0.2.0 Production"
TNS for Solaris: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production
With its 12c release, Oracle has added the functionality to allow the declaration of Pl/SQL functions directly at the top of an SQL statement (see https://oracle-base.com/articles/12c/with-clause-enhancements-12cr1)
This can be quite a handy feature, esp. on projects where you need to pull data from DBs with user rights limited to SELECT statements.
The following (obviously simplified) query runs fine in Oracle SQL Developer:
with
function add_string(p_string in varchar2) return varchar2
is
--Function to add a string
l_buffer varchar2(32767);
begin
l_buffer := p_string || ' works!';
--
return l_buffer;
--
end ;
--
select add_string('Yes, it') as outVal
from dual
;
---------
OUTVAL
Yes, it works!
Now, I am trying to load the result set of a query based on the same principle into an ADODB Recordset in MS-Access 2007. I am aware that ADO does not handle queries starting with a WITH clause seamlessly (see, e.g. Why can't I do a "with x as (...)" with ADODB and Oracle?). The way I usually get around this problem is to enclose the query in a SELECT block, like this:
select hey_yo
from (
with sub as (
select 'hey' as hey
from dual
)
select hey || ' yo!' as hey_yo
from sub
)
;
---------
HEY_YO
hey yo!
Unfortunately, this does not seem to be legal syntax in Oracle when dealing with functions in the WITH clause:
select *
from (
with
function add_string(p_string in varchar2) return varchar2
is
--Function to add a string
l_buffer varchar2(32767);
begin
l_buffer := p_string || ' works!';
--
return l_buffer;
--
end ;
--
select add_string('Yes, it') as outVal
from dual
)
;
---------
PLS-00103: Encountered the symbol ")" when expecting one of the following:
. , # ; for <an identifier>
<a double-quoted delimited-identifier> group having intersect
minus order partition start subpartition union where connect
sample
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
Here's the sub I'm trying to run in VBA:
Sub TestSub()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
'Connection details
Dim strHostName As String
Dim nPortNum As Integer
Dim strUser As String
Dim strPassword As String
Dim strServiceName As String
Dim strConnection As String
Dim strSQL As String
Set conn = New ADODB.Connection
'[... set credentials ...]
'Open Connection
With conn
.ConnectionString = "Provider=MSDAORA;" & _
"Data Source=(DESCRIPTION=(ADDRESS_LIST=" & _
"(ADDRESS=(PROTOCOL=TCP)(HOST=" & strHostName & ")(PORT=" & nPortNum & ")))(CONNECT_DATA=(SERVICE_NAME=" & strServiceName & ")));" & _
"User ID=" & strUser & ";Password=" & strPassword & ";"
.Open
End With
Set rs = New ADODB.Recordset
strSQL = "WITH FUNCTION add_string(p_string IN VARCHAR2) RETURN VARCHAR2 IS l_buffer VARCHAR2(32767); BEGIN l_buffer := p_string || ' works!'; RETURN l_buffer; END ; SELECT add_string('Yes, it') AS outval FROM dual"
rs.Open strSQL, conn, adOpenStatic, adLockReadOnly
'[... do stuff with data ...]
rs.MoveFirst
Debug.Print rs.Fields(0).Value
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
End Sub
Any ideas how to get around this problem? (Unfortunately, compiling the function in the DB is not an option for this particular project).
Update:
I should mention the error I get when running the VBA code:
Run-Time error 3704: Operation is not allowed when the object is
closed.
at rs.MoveFirst
Use the "Oracle Provider for OLE DB" instead of "Microsoft OLE DB Provider for Oracle". Oracle provider can be downloaded from here: 32-bit Oracle Data Access Components (ODAC) and NuGet Downloads
The Microsoft provider has been deprecated for many years, it is not further developed and should not be used anymore. Current release of Oracle provider is 12.1, i.e. it should support also new Oracle 12c features.
Connection string would be Provider=OraOLEDB.Oracle; ... instead of Provider=MSDAORA;...
Unfortunately I can't test the following because I have only an Oracle 11 at my disposal here.
In theory it should work. But you should use a ADODB.Command to send the SQL as it is, straight away to Oracle
Try this :
Dim strConnection As String
Dim strSQL As String
Dim cmdQuery As ADODB.Command
Set conn = New ADODB.Connection
'[... set credentials ...]
'Open Connection
With conn
.ConnectionString = "Provider=MSDAORA;" & _
"Data Source=(DESCRIPTION=(ADDRESS_LIST=" & _
"(ADDRESS=(PROTOCOL=TCP)(HOST=" & strHostName & ")(PORT=" & nPortNum & ")))(CONNECT_DATA=(SERVICE_NAME=" & strServiceName & ")));" & _
"User ID=" & strUser & ";Password=" & strPassword & ";"
.Open
End With
Set rs = New ADODB.Recordset
strSQL = "WITH FUNCTION add_string(p_string IN VARCHAR2) RETURN VARCHAR2 IS l_buffer VARCHAR2(32767); BEGIN l_buffer := p_string || ' works!'; RETURN l_buffer; END ; SELECT add_string('Yes, it') AS outval FROM dual"
Set cmdQuery = New ADODB.Command
cmdQuery.ActiveConnection = conn
cmdQuery.CommandText = strSQL
With rs
.LockType = adLockPessimistic
.CursorType = adUseClient
.CursorLocation = adUseClient
.Open cmdQuery
End With
'[... do stuff with data ...]

Execute stored procedure and return resultset

I'm a complete VBScript newbie and I am trying to execute a stored procedure and read the resultset. I have tried numerous different approaches using articles online but nothing works and I'm stumped. The database is SQL Server 2008 R2, the application is an off-the-shelf ERP system but I'm able to add my own code to it.
I can execute code by using:
connection.execute"insert into blah blah blah"
And I can read result set by using:
Set objRS = CreateObject("ADODB.Recordset")
objRS.Open "select a, b, c FROM blahblah", Connection, adOpenStatic, adLockBatchOptimistic, adCmdText
If objRS.EOF = False Then
a = objRS.Fields("a").Value
b = objRS.Fields("b").Value
c = objRS.Fields("c").Value
End If
objRS.Close
The stored procedure in question is in effect a select statement e.g.:
create procedure [dbname].[dbo].[sptestproc]
as
#Option1 Varchar(10) = NULL,
#Option2 Varchar(10) = NULL
AS
BEGIN
select first, second
from table1
where a = #option1 and b = #toption2
End
My code so far:
Dim sql
sql = "EXEC [dbname].[dbo].[sptestproc] '" & Opt1 & "','" & Opt2 & "'"
Set RS = CreateObject("ADODB.Recordset")
RS.Open sql, Connection, adOpenStatic, adLockBatchOptimistic, adCmdText
Do While Not RS.EOF
Call App.MessageBox("first",vbInformation,"Data updated")
Call App.MessageBox("second",vbInformation,"Data updated")
RS.MoveNext
Loop
But I cannot for the life of me get a procedure to execute and read the results.
Can anyone help?
Thanks
adCmdText would be for SQL query if you want to execute a stored procedure then you have to use adCmdStoredProc (value 4 instead)
EDIT:
'Set the connection
'...............
'Set the command
DIM cmd
SET cmd = Server.CreateObject("ADODB.Command")
SET cmd.ActiveConnection = Connection
'Set the record set
DIM RS
SET RS = Server.CreateObject("ADODB.recordset")
'Prepare the stored procedure
cmd.CommandText = "[dbo].[sptestproc]"
cmd.CommandType = 4 'adCmdStoredProc
cmd.Parameters("#Option1 ") = Opt1
cmd.Parameters("#Option2 ") = Opt2
'Execute the stored procedure
SET RS = cmd.Execute
SET cmd = Nothing
'You can now access the record set
if (not RS.EOF) THEN
first = RS("first")
second = RS("second")
end if
'dispose your objects
RS.Close
SET RS = Nothing
Connection.Close
SET Connection = Nothing
Connection.Execute "SET NOCOUNT ON"
Before
SET cmd.ActiveConnection = Connection
I went well.
Dim sql
Set RS = CreateObject("ADODB.Recordset")
Set cmd = CreateObject("ADODB.Command")
Set prm = cmd.CreateParameter("#option1", adVarChar, adParamInput, , Opt1)
cmd.Parameters.Append prm
Set prm = cmd.CreateParameter("#option2", adVarChar, adParamInput, , Opt2)
cmd.Parameters.Append prm
cmd.CommandText = "[dbname].[dbo].[sptestproc]"
Set Com.ActiveConnection = Connection
'Connection must be opend already
RS.Open cmd, , adOpenStatic, adLockBatchOptimistic
Do While Not RS.EOF
Call App.MessageBox("first",vbInformation,"Data updated")
Call App.MessageBox("second",vbInformation,"Data updated")
RS.MoveNext
Loop

Calling an oracle stored proc that returns a strong record type

I have a stored proc in oracle that looks like this:
PROCEDURE get_protection_details (
i_case_key IN NUMBER,
i_eff_dt IN DATE,
protection_rec OUT protection_rectype
)
The output parameter is type record - declared in the package like this:
TYPE crms_protection_rectype IS RECORD (
active_protection_flag VARCHAR2(1) := NULL,
protection_type VARCHAR2(30) := NULL,
term VARCHAR2(30) := NULL,
protection_fee_name VARCHAR2(30) := 'PROTECTION'
);
I am trying to call this from VB6 - here is the code I have
Dim adoCMD As Object
Dim rs as ADODB.recordset
Set adoCMD = CreateObject("ADODB.Command")
adoCMD.ActiveConnection = Me.Cn
adoCMD.CommandText = "fdp$product.get_protection_details"
adoCMD.CommandType = adCmdStoredProc
adoCMD.Parameters.Append adoCMD.CreateParameter("i_case_key", adDouble, adParamInput, 32000, plCaseKey)
'//this case key is type long and value is 20305003'
adoCMD.Parameters.Append adoCMD.CreateParameter("i_eff_dt", adDate, adParamInput, 32000, Format(Now(), "DD/MM/YYYY"))
rs = adoCMD.Execute
I get an error saying incorrect number of parameters - tearing my hair out over something that should be pretty easy - how do you call a stored proc that returns a defined record type?
Have examples everywhere that returns a weak cursor in this way.
Is there a way to define the record in vb6 somehow?
The problem here is that you haven't defined the output parameter.
adoCMD.Parameters.Append adoCMD.CreateParameter("protection_rec", adXXXX, adParamOutput, nnnnn)
The only thing is, I don't know if you can do this, and with what type. Maybe adLongVarBinary would work, and you would have to copy chunks of data out of the resulting buffer.
Why can't you simply return the data as a standard recordset, i.e. change your stored procedure to do the following:
SELECT active_protection_flag, protection_type, term, protection_fee_name
If there is a good reason not to change your stored procedure's interface, maybe you could create a wrapper stored procedure which essentially converts the output parameters into a SELECT statement.

With ADO, how do I call an Oracle PL/SQL block and specify input/output bind variables (parameters?)

I am trying to call a PL/SQL block with ADO and VBA, but I can't pass input and/or output bind variables (probably aka parameters).
dim cn as ADODB.connection
' ... open connection ...
dim plsql as string
plsql = "declare"
plsql = plsql & " num_in number := ?;"
plsql = plsql & " num_out number; "
plsql = plsql & "begin"
plsql = plsql & " num_out := num_in * 5;"
plsql = plsql & " ? := num_out;"
plsql = plsql & "end;"
dim cm as ADODB.command
set cm = new ADODB.command
set cm.activeConnection = cn
cm.commandText = plsql
cm.commandType = adCmdText
cm.parameters.append cm.createParameter(, adDouble, adParamInput,, 5)
cm.parameters.append cm.createParameter(, adDouble, adParamOutput )
cm.execute ' FAILS HERE
msgBox(cm.parameters(2))
The snippet above fails at the cm.execute line with an ORA-01008: not all variables bound
I'd appreciate any help towards a solution for my problem.
It seems as though the statement cannot start with a declare. (Thanks to Thomas Jones-Low for his valuable comment).
So, the statement must be enclosed in another begin .. end block:
' additional begin so that the statement does not start with a declare:
plsql = "begin "
plsql = plsql & "declare"
plsql = plsql & " num_in number := ?;"
plsql = plsql & " num_out number; "
plsql = plsql & "begin"
plsql = plsql & " num_out := num_in * 5;"
plsql = plsql & " ? := num_out;"
plsql = plsql & "end;"
' closing the additional begin:
plsql = plsql & "end;"
Now, it works as expected.

Resources