I am migrating a VC++/SQL server app to using Oracle. The database access is implemented using ADO classes, and I can't find a way to go through the cursor that is returned by Oracle.
The sproc is something like:
create or replace PROCEDURE GetSettings
(
cv_1 OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN cv_1 FOR
SELECT KEY ,
VALUE
FROM Settings;
END;
The code is something like:
_CommandPtr pCommand;
_ParameterPtr pParam1;
HRESULT hr = pCommand.CreateInstance (__uuidof (Command));
if (FAILED (hr))
return;
pCommand->ActiveConnection = m_pConn;
pCommand->CommandText = "GetSettings";
pCommand->CommandType = adCmdStoredProc;
_RecordsetPtr pRecordset;
hr = pRecordset.CreateInstance (__uuidof (Recordset));
if (FAILED (hr))
return;
pRecordset = pCommand->Execute(NULL,NULL,adCmdStoredProc);
(in fact it is using the ADO classes from http://www.codeproject.com/Articles/1075/A-set-of-ADO-classes-version-2-20#TheSample02 )
The returned pRecordset is in a closed state and you cannot do anything with it. I imagine I should pass some parameter for the cursor, but how do you create/use/access the returned cursor using these ADO functions? There is no cursor parameter type that I can see
I am completely stuck and would greatly appreciate some help
Thanks
Finally found out how to do it, you need specify special parameters in the connection string to tell it to return result set:
Provider=ORAOLEDB.ORACLE;User ID=xxx;Password=xxx;Data Source=tns_name;OLEDB.Net=True;PLSQLRSet=True;
Related
I have the following stored procedure:
ALTER procedure [dbo].[spx_kasir_verifikator_GetData_web]
#id_verifikator int
as
begin
SELECT * FROM tb_kasir_set_verifikator
WHERE tb_kasir_set_verifikator.id_verifikator = id_verifikator;
end
Controller:
public function show($id_verifikator)
{
$setverifikator = DB::select("exec spx_kasir_verifikator_GetData_web ?",[$id_verifikator]);
dd($setverifikator);
}
And I'm trying to call this procedure in Laravel 8, I need to display just one id or by id_verifikator but it's always showed all data. How I solve this?
It's returning everything as you are using select * which tells the database that you want the entire row. If you wish to only get a specific column then you need to change that to select id_verifikator.
A bit off-topic but I would suggest that you just run this query in Laravel instead of having a procedure, especially as this is such a basic query. The below links can help you get started.
https://laravel.com/docs/8.x/queries
https://laravel.com/docs/8.x/eloquent
I have the pleasure of maintaining a legacy application using Classic.ASP for the frontend and an Oracle database for the backend.
We have an ongoing issues where we need to routinely update queries like the following to have an ever increasing value for the 'resultset' parameter
Set cmdStoredProc = Server.CreateObject("ADODB.Command")
cmdStoredProc.CommandText = "{call package_name.Procedure_Name(?,{resultset 1500, v_out_one, v_out_two})}"
It started at 500, then a bug fix made it 1000, then 1500, and now it has became an issue again on my watch.
Rather than follow in my predecessor's footsteps and arbitrarily increase it I'd like to know as much as possible about this feature but am struggling to find any documentation on it.
Is there a specific name given to this feature / argument / parameter? Knowing this should be enough to allow me to find out more about it but a brief explanation of it or link to documentation on it would be advantageous.
From the comments / answers it has become apparent that having the definition of the procedure that is being called could be useful:
PROCEDURE Procedure_Name
(n_site_id_in IN TABLENAME.site_org_id%TYPE,
v_out_one OUT t_c_out_one,
v_out_two OUT t_c_out_two)
IS
--Select the CC and account code and descriptions into a cursor
CURSOR c1 IS
SELECT a.out_one,
a.out_two
FROM TABLENAME a
WHERE a.site_org_id = n_site_id_in
ORDER BY a.out_one, a.out_two;
i INTEGER DEFAULT 1;
BEGIN
FOR get_c1 IN c1 LOOP
v_out_one(i) := get_c1.out_one;
v_out_two(i) := get_c1.out_two;
i := i + 1;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('no data found');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('sqlerrm '||SQLERRM);
RAISE;
END Procedure_Name;
From this we can see the procedure has 3 parameters defined, 1 IN and 2 OUT, yet the call to the procedure seems to convert the 2 OUT parameters to a collection based on resultset.
The driver in use is 'Microsoft ODBC for Oracle' (MSORCL32.DLL)
Your procedure package_name.Procedure_Name must return a cursor as an out parameter.
This resultset parameter let me think of a parameter defining the number of cursors that can be open at the same time.
The fact is it does not seem to be the right way of doing things because it means that each time the procedure is called, the cursor is not closed.
In your code you must have stg like
Set myRecordSet = cmdStoredProc.Execute()
This recordset is used to read the cursor content.
Please check that it is closed after usage with
myRecordSet.Close()
Set myRecordset = Nothing
The 'resultset' argument does not have any special name, it is just known as the resultset parameter.
There are multiple ways it can be used:
Return all the columns in a single result set (as it currently is):
Set cmdStoredProc = Server.CreateObject("ADODB.Command")
cmdStoredProc.CommandText = "{call package_name.Procedure_Name(?,{resultset 1500, v_out_one, v_out_two})}"
Return each column as a single result set (to return 2 separate result sets):
Set cmdStoredProc = Server.CreateObject("ADODB.Command")
cmdStoredProc.CommandText = "{call package_name.Procedure_Name(?,{resultset 1500, v_out_one}, {resultset 1500, v_out_two})}"
Read more about it here: https://learn.microsoft.com/en-us/sql/odbc/microsoft/returning-array-parameters-from-stored-procedures
As assumed, it is used to set the limit on the amount of records that can be returned from the procedure call.
The definition of the procedure shows that it is returning 2 arrays as output so an error will be thrown if either of them exceeds the limit set in the resultset parameter.
All.
We have an Oracle package that returns a ref cursor:
CREATE OR REPLACE PACKAGE BODY sandbox AS
FUNCTION my_function (text VARCHAR2) RETURN result_cv IS result result_cv;
BEGIN
OPEN result FOR SELECT MLS_SID FROM MLS;
RETURN result;
END;
END sandbox;
I am calling the function with the following scala code:
lazy val database = Database.forDataSource(DB.getDataSource())
database withSession {
val x = sql"select sandbox.my_function($text) from DUAL".as[(Int)]
x foreach (x => println(x))
Ok(String.valueOf(x.first))
}
The code fails with the following error:
[SQLException: Invalid column type: getInt not implemented for class oracle.jdbc.driver.T4CResultSetAccessor]
The SQL statement works when I just use the select statement that is in the function (SELECT MLS_SID FROM MLS;), but when I open it as a ref cursor and return the ref cursor it fails. I looked at the T4CResultSetAccessor and it only has one method getBytes().
Can anyone offer suggestions on how to make this work using the Oracle function call and ref cursors? Thanks in advance.
-patrick
Frome Typesafe:
Slick doesn’t support OUT parameters at the moment (which you would
need to properly return a ref cursor). If the cursor is all you need
to return from your stored proc, I suggest to use the method
recommended in the SO post: Lift the result set to the top level with
TABLE(). Does this work for your use case?
So our solution was to do this:
SELECT * FROM TABLE(my_function('text'))
We got an error, but that was Lucas' and Typesafe's suggestion
i have a vb6 application that i am using DAO to create a connection to a database and trying to open a recordset. the database is a foxpro database and i have refernece to Microsoft DAO 2.5/3.5 Compatibility Library.
my code is as follows
Dim gdbSMS As Database
If gdbSMS Is Nothing Then
Set gdbSMS = OpenDatabase("C:\Work\M2M Test\DATA", False, False, "Foxpro 2.6;")
End If
Dim sql As String
sql = "select *, substr(lineitem,8,6) as aa from shippers where shipper = '001322' order by aa"
Dim rsShipper As DAO.Recordset
Set rsShipper = gdbSMS.OpenRecordset(sql)
Do While Not rsShipper.EOF
Beep
rsShipper.MoveNext
Loop
rsShipper.Close
when i execute teh line for openrecordset i get an error "undefinied function 'substr' in expression
i run the exact same query in foxpro and it works fine. any thoughts on what i need to do to get this to work with substring functions?
thanks
Try using the T-SQL SUBSTRING function instead.
SUBSTRING ( value_expression , start_expression , length_expression )
Without using DAO recordsets, but instead using DataTables, and data adapters using OleDBProvider for Foxpro data (definitely not going back to Fox 2.x) gives you more current flexibility in querying....
That said, you could try by doing what SUBSTR() actually does... Try changing to
RIGHT( LEFT( LineItem, 14 ), 6 ) as AA
I'm trying to execute a SQL query through ODP.NET to create a table, but I always get an ORA-00911 'invalid character' error. The Errors object in the exception always has the text "ORA-00911: invalid character\n", even if there are no linebreaks in the SQL query itself.
The code I'm executing the SQL is this:
using (OracleConnection conn = new OracleConnection(<connection string>) {
using (OracleCommand command = conn.CreateCommand()) {
conn.Open();
command.CommandText = queryString;
command.ExecuteNonQuery(); // exception always gets thrown here
}
queryString contains a single CREATE TABLE statement, which works fine when executed through SQL Developer
EDIT: the SQL I am executing is this:
CREATE TABLE "TESTSYNC"."NEWTABLE" (
"COL1" NUMBER(*,0) NULL,
"COL2" NUMBER(*,0) NULL
);
with linebreaks removed
Other people have come across this issue - ODP.NET does not support multiple SQL statements in a text command. The solution is to wrap it in a PL/SQL block with EXECUTE IMMEDIATE around each statement. This lack of support for ; seems incredibly boneheaded to me, and has not improved my opinion of the oracle development team.
Furthermore, this seems to be an issue with oracle itself, as I have the same problems with the MS and ODBC oracle clients.
I had this issue for some reason you have to have code on one line.
I had strSQL = "stuff" +
" more stuff"
I had to put it on one line.
strSQL = "stuff more stuff"
It some how reads the cr/lf.
Wrap your sql in a Begin block.
Dim sqlInsert As String = ""
For i = 1 To 10
sqlInsert += "INSERT INTO MY_TABLE (COUNT) VALUES (" & i & "); "
Next
Call ExecuteSql("BEGIN " & sqlInsert & " END;")
Your quotes are OK (it just forces Oracle to treat your object names as case sensitive i.e. upper case the way you've written it) but I'm not at all sure you're allowed to define NUMBER that way with a *.
I wonder if it is the "*" in the sql have you tried the call without an * in the create? I bet it is yet another "feature" of the ODP.Net driver