What is the name of the 'resultset' argument in an ADODB.Command calling an Oracle stored procedure in Classic.ASP? - oracle

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.

Related

Determine whether a SAS dataset is a table or view

I'm trying to determine, given a SAS dataset's name, whether it is a table or view.
The context is that I have a data step where I iterate over a list of dataset names, and if the dataset is a table (and not a view) I'd like to perform a call execute to a sql procedure which drops the table whose name is specified. As it stands now, the code works as intended but throws several warnings of the form
WARNING: File WORK.datasetname.DATA does not exist.
Here is the code I'm using:
data _null_;
set work.ds_list;
tbl_loc = scan(tbl_name,1,'.');
if(tbl_loc = 'WORK') then do;
drop_string = catx(' ',
'proc sql; drop table',
tbl_name,
'; quit;');
call execute (drop_string);
put ' ** Queueing call to drop table ' tbl_name;
end;
run;
So how do I determine from the dataset's name whether it is a view or table?
Thanks!
The function EXIST function will help you here.
if exist(tbl_name,'DATA') then memtype = 'TABLE'; else
if exist(tbl_name,'VIEW') then memtype = 'VIEW';
drop_statements = catx
( ' ',
'proc sql; drop', memtype, tbl_name, '; quit;'
);
From Docs
Syntax
EXIST(member-name <, member-type <, generation>>)
Required Argument
member-name
is a character constant, variable, or expression that specifies the
SAS library member. If member-name is blank or a null string, then
EXIST uses the value of the LAST system variable as the member name.
Optional Arguments
member-type
is a character constant, variable, or expression that specifies the
type of SAS library member. A few common member types include ACCESS,
CATALOG, DATA, and VIEW. If you do not specify a member-type, then the
member type DATA is assumed.
Rather than 'create it' how about using SASHELP.VTABLE to determine if it's a VIEW or DATA.
data temp /view=temp;
set sashelp.class;
run;
data check;
set sashelp.vtable;
where libname='WORK';
run;
Note that the memtype in this case is VIEW. You could probably join your data set to the table as well or do some form of lookup, but a join would be pretty straightforward.
Then once you have the data sets, you can use a PROC DATASETS to drop them all at once rather than one at a time. You don't indicate what initially created this list, but how that list is created is important and could possibly simplify this a lot.
proc datasets lib=work;
delete temp / memtype=view;
run;quit;
so - you'd like to delete all datasets, but not views, from a library?
Simply use the (documented) delete procedure:
proc delete lib=work data=_all_ (memtype=data) ;
run;

Oracle Stored Procedures with ADO NOT .net and VC++

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;

Tuning query with VARCHAR2 column

There is this stored procedure that builds a dynamic query string and then execute it. The sp works fine in development and testing environment, but the DBA of the client company has informed that this query is hitting really hard to the database in production. The IT area has asked us to tune up the query. So far so good, we've moved almost all this sp from building the query string dynamically into a single big query that performs really fast (compared to the old query).
We have found (among other things) that the sp built the where clause of the query string by evaluating if a parameter has a default value or a real value i.e.
IF P_WORKFLOWSTATUS <> 0 THEN
L_SQL := TRIM(L_SQL) || ' AND WORKFLOW.STATUS = ' || TO_CHAR(P_WORKFLOWSTATUS);
END IF;
So we optimized this behavior to
WHERE
...
AND (WORKFLOW.STATUS = P_WORKFLOWSTATUS OR P_WORKFLOWSTATUS = 0)
This kind of change has improved the query that affected numeric columns, but we have found a problem with a VARCHAR2 parameter and column. The current behavior is
--CLIENT.CODE is a VARCHAR2(14) column and there is an unique index for this column.
--The data stored in this field is like 'N0002077123', 'E0006015987' and similar
IF NVL(P_CLIENT_CODE, '') <> '' THEN
L_SQL := TRIM(L_SQL) || ' AND CLIENT.CODE = ''' || P_CLIENT_CODE || '''';
END IF;
We tried to change this to our optimized version of the query by doing
WHERE
...
AND (CLIENT.CODE = P_CLIENT_CODE OR NVL(P_CLIENT_CODE, '') = '')
but this change made the query lost performance. Is there a way to optimize this part of the query or should we turn our big query into a dynamic query (again) just to evaluate if this VARCHAR2 parameter should be added or not into the where clause?
Thanks in advance.
Oracle treats empty strings '' as NULL. So this condition NVL(P_CLIENT_CODE, '') = '' doesn't really make much sense. Moreover it will always be false, because here we are checking equality of NULLs, which is always false. To that end you might and probably should recode that part of the query as:
WHERE
...
AND ( (CLIENT.CODE = P_CLIENT_CODE) OR (CLIENT IS NULL) )
I recommend or to move this varchar2 parameters back to dynamic, or to use the following:
WHERE
...
AND CLIENT.CODE = nvl(P_CLIENT_CODE,CLIENT.CODE)
and be sure you have index on client.code.(Or the table partitioned on client.code, if possible.)
Of course, as it has already been said, you need need to perform correct null checks.
However, the trick is, the difference between
AND (CLIENT.CODE = P_CLIENT_CODE OR NVL(P_CLIENT_CODE, '') = '')
and
AND ( (CLIENT.CODE = P_CLIENT_CODE) OR (CLIENT IS NULL) )
is very unlikely to cause performance problems only by itself. I would even say that query with second clause could perform worse than with the first one, as it will yield true for more rows, resulting in larger result set for consequent joins/orders/filers etc.
I'd bet that adding this clause to your query somehow breaks its optimal execution plan. For instance, having obsolete statistics, the optimizer could make a sub-optimal decision to choose unselective index on client.code instead of others available.
However, it is hard to tell for sure without seeing actual (not the expected one, which you obtain with explain plan command!) execution plan of slow query and your table structure.

Oracle db gives ORA-01722 for seemingly NO REASON AT ALL

I'm trying to use an Oracle database with ado.net, and it is proving a painful experience. I use Oracle Client (Oracle.Data namespaces).
The following query runs fine from a query window:
UPDATE PRINT_ARGUMENT
SET VALUE = 'Started'
WHERE REQUEST_ID = 1 AND KEYWORD = '{7D066C95-D4D8-441b-AC26-0F4C292A2BE3}'
When I create an OracleCommand however the same thing blows up with ORA-01722. I can't figure out why.
var cmd = cnx.CreateCommand();
cmd.CommandText = #"
UPDATE PRINT_ARGUMENT
SET VALUE = :value
WHERE REQUEST_ID = :requestID AND KEYWORD = :key";
cmd.Parameters.Add(new OracleParameter("requestID", (long)1);
cmd.Parameters.Add(new OracleParameter("key", "{7D066C95-D4D8-441b-AC26-0F4C292A2BE3}");
cmd.Parameters.Add(new OracleParameter("value", "Started");
cnx.Open();
try { int affected = cnx.ExecuteNonQuery(); }
finally { cnx.Close(); }
When I inspect the command in the debugger, the parameters appear to have mapped to the correct types: requestID has OracleDbType.Int64, key and value are both OracleDbType.Varchar2. The values of the parameters are also correct.
This gets even stranger when you consider that I have other queries that operate on the exact same columns (requestID, keyword, value) using the same approach - and they work without a hiccup.
For the record, the column types are requestID NUMBER(10,0); key VARCHAR2(30); value VARCHAR2(2000).
According to Oracle, ORA-01722 'invalid number' means a string failed to convert to a number. Neither of my string values are numbers, neither of the OracleParameters created for them are numeric, and neither
By default, ODP.NET binds parameters by position, not by name, even if they have actual names in the SQL (instead of just ?). So, you are actually binding requestID to :value, key to :requestID and value to :key.
Correct the order of cmd.Parameters.Add in your code, or use BindByName to tell ODP.NET to use the parameter names.
Since you are using named parameters, you have to tell the Oracle client about it. Otherwise your parameters are mixed up (key is assigned to :value):
OracleParameter parameter = new OracleParameter("requestID", (long)1);
parameter.BindByName = true;
cmd.Parameters.Add(parameter);
It's a strange and unexpected behavior, but that's how it is.

Cannot executing a SQL query through ODP.NET - invalid character error

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

Resources