Call a parameterized Oracle query from ADODB in Classic ASP - oracle

I’m currently working on a classic ASP project talking to an Oracle database. I’m trying to find a way to safely call an Oracle PL/SQL script and passing parameters with ADO. The currently solution builds the SQL script by hand with embedded variables like this:
strSQL = "SELECT field1, etc FROM my_table WHERE (field = '" & filter_value & "')"
This, of course, is ugly and insecure, and open to abuse.
The code that I have so far (purloined from various non classic asp based web sites) looks like this:
dim strSQL, oConn, oCommand, oParam
set oConn = server.createobject("ADODB.Connection")
oConn.Open myConnString
strSQL = "SELECT field1, etc FROM my_table WHERE (field = :filter_field)"
dim oFilteredList
set oFilteredList = Server.CreateObject("ADODB.Command")
oFilteredList.ActiveConnection = oConn
oFilteredList.CommandText = strSQL
oFilteredList.CommandType = adCmdText
oFilteredList.NamedParameters = True
set oParam = oFilteredList.CreateParameter("filter_field", adVarChar, adParamInput, 10, filter_value)
oFilteredList.Parameters.Append oParam
set rsResults = oFilteredList.Execute
This causes the error “Parameter object is improperly defined. Inconsistent or incomplete information was provided”
What is the correct method of calling Oracle / PL/SQL with named parameters from ADO? I need to use named parameters because the actual SQL code is somewhat more complex, and different parameters are used multiple times throughout the SQL command.

How do you have filter_value defined? If it's not declared as a String or if you've assigned a string longer than 10 characters (as you've indicated when creating the parameter), you'll have issues with that.
Additionally (and partly for my own reference), named parameters are not supported via OraOLEDB (i.e. ADODB).
See Oracle® Provider for OLE DB Developer's Guide 11g Release 1 (11.1) or follow the "Command Parameters" heading link on any of the previous versions (8iR3, 9i, 9iR2, 10g, 10gR2):
Command Parameters
When using Oracle ANSI SQL, parameters
in the command text are preceded by a
colon. In ODBC SQL, parameters are
indicated by a question mark (?).
OraOLEDB supports input, output, and
input and output parameters for PL/SQL
stored procedures and stored
functions. OraOLEDB supports input
parameters for SQL statements.
"Note: OraOLEDB supports only
positional binding."
That said, this should have no bearing on your query when using OraOLEDB:
oFilteredList.NamedParameters = True
I've had success running queries exactly as the rest of your example shows though on Oracle 10gR2.
You don't show your connection string, so I must assume it to be valid. Behavior can differ depending on options there, so here's what I successfully use:
`"Provider=OraOLEDB.Oracle;Data Source=TNSNAMES_ENTRY;User ID=XXXX;Password=YYYY;DistribTx=0;"`

Related

Unable to pass parameters with periods in Oracle APEX / REST

Using Oracle APEX 5.0.4, with Oracle 12c. I am trying to write a REST service in Oracle APEX. I have a SQL statement...
select * from <my_table> where company_name = 'GOOGLE INC.'
When run from a straight SQL prompt, this runs and returns data. I have tried to implement this in a REST service, specifically using a URI template. I am using a bind variable, so my query now looks like.
select * from <my_table> where company_name = :ACCT_NAME;
Both the space and the period in my company name are causing me problems, returning no rows. If my BIND VARIABLE is a single word, then no problems. I found UTL_URL.UNESCAPE, and have tried to use that. This seems to solve the problem of having a space, but does not fix issue when the company name contains a period.
I rewrote my SQL Statement to
select * from <my_table> where company_name = UTL_URL.UNESCAPE(:ACCT_NAME);
The query will work if the BIND VARIABLE (aka company name) is multiple words, but NOT containing a period. For example, I can look up 'GOOGLE INC' by setting my bind variable = 'GOOGLE%20INC' . I need to be able to look up 'GOOGLE INC.' [notice the period at the end of INC] so I set my bind variable = 'GOOGLE%20INC%2E' and I consistently get 'BAD REQUEST'. It appears that the URI template is not valid.
If I pass this in via a straight SQL statement (aka OUTSIDE of the REST service), my data is found...
select * from <my_table> where company_name = UTL_URL.UNESCAPE('GOOGLE%20INC%2E');
There is something unique about the PERIOD character... Any ideas?

Microsoft ODBC Driver for Oracle, slow for view not for table (Excel VBA)

We are forced to use the Microsoft ODBC Driver for Oracle as our means of connecting Excel VBA over ADO to an Oracle 11GR2 database.
We have noticed it is incredibly slow returning data from a relatively simple view (the view executes quickly in SQL Developer)...but if we derive a table from the view results...and execute a query against the table, then Excel returns data in about 10 seconds. It is returning 11,000 records.
When we are going through a view, we see the data starting to be returned (so the view has done its job and executed), but then to the point of the recordset copy into the worksheet completing - it takes 6 minutes.
Use the table derived from the view, and it takes 10 seconds.
I would love the luxury of using the oracle oledb driver (don't ask), but can't so need to resolve this if possible.
strConOracle = "Driver={Microsoft ODBC for Oracle}; " & _
"CONNECTSTRING=(DESCRIPTION=" & _
"(ADDRESS=(PROTOCOL=TCP)" & _
"(HOST=" & "myserver.mycompany.net" & ")(PORT=1524))" & _
"(CONNECT_DATA=(SERVICE_NAME=" & SID & "))); uid=" & username & " ;pwd=" & password & ";"
'--- Now connection is open and you can use queries to execute them.
'--- It will be open till you close the connection
'sql = "Select * from TEMP_TABLE_FROM_MY_VIEW" VERY FAST < 10 seconds 11,000 rows
sql = "Select * from MY_VIEW"
Sheet1.Cells.ClearContents
Set rs = con.Execute(sql)
'6 minutes executing the view, with data seen in worksheet very quickly (view resolved)
Sheet1.Range("A2").CopyFromRecordset rs
UPDATE: It seems related to large character widths. So we have varchar2(4000) fields, containing ~2-300 characters (we are replicating another data environment). Oledb works fine, but this ODBC driver - it kills it. We are casting to varchar2(300) to get better performance, but it is still slow (but an improvement).

vb6 oracle9i application, logical error-recordset remains empty

Currently I am working on a project that forces me to use Vb6 as a front end and Oracle 9i as a backend in my application. I need to retrieve data from a table in oracle, and display it in a VB6 form. Im using a recordset for this, but for some reason it doesnt contain any record even when a valid record is present in the table in question. Can anyone tell me what's wrong? Any help would be appreciated. My code is as follows-
Private Sub Command1_Click()
Dim sql As String
Set rs = New ADODB.Recordset
sql = "select test23.phoname from test23 where test23.ops='" + Text1.Text + "'"
rs.Open sql, con, adOpenStatic, adLockOptimistic
If rs.EOF = False Then
Form7.Show
Form7.Label2.Caption = rs.Fields("phoname")
End If
End Sub
The if statements never execute, as the recordset always seems empty. If I remove the EOF condition I get a runtime error 3021. Is there something wrong with my sql query? The table test23 is already present in my oracle database with attributes serialno, phoname and ops.
Thanks-
Ron
I have struggled through this myself some years ago. The problem could be in several areas: specificaly the connection, the Oracle drivers, the Oracle home or the query itself.
I would strongly recommend that you download the free version of TOAD which will allow you to test both your connection (I assume ODBC of some sort) and you can then execute the query manually using TOAD.
Are you getting any exceptions?
Hopefully from this you can then correct your code/connection/query.
Good luck
I would also point out that adoOpenStatic and adoLockOptimistic are mutually exclusive property values, as optimistic locking only applies to updatable recordsets and static recordsets aren't updatable (except in batch mode with adoLockBatchOptimistic). This may be confusing the Oracle OLEDB provider somehow.

Different parameter sizes result in inefficient query plan cache

Nhibernate profiler shows lots of error messages about the query plan:
Different parameter sizes result in inefficient query plan cache usage
It also leads you to an explanation in http://nhprof.com/Learn/Alerts/UncachedQueryPlan and warns you about the use of prepare_sql = true parameter when building session. I do it that way with fluent:
.ExposeConfiguration(configuration => configuration
.SetProperty("current_session_context_class", "thread_static")
.SetProperty("prepare_sql", "true")
.SetProperty("generate_statistics", "true")
)
But it seems that it isn't working as error messages are still there. Is that a limitation on OracleClientConfiguration or am I doing it wrong?
Edit To provide with some more information about this...
In my repository I do this
session.Query<TEntity>.Where(predicate).ToList();
and this is the call
var value = ParameterRepository.First(p => (p.Pipeline.Id == pipelineId && p.Name == name));
For instance those are two SQL generated from this call and that nhibernate profiler shows as "DIfferent parameter sizes result in inefficient query plan cache usage"
select GUID1_12_,
PARAMETER2_12_,
PARAMETER3_12_,
GUID4_12_
from (select pipelineex0_.GUID_PIPELINE_EXEC_PARAMETER as GUID1_12_,
pipelineex0_.PARAMETER_NAME as PARAMETER2_12_,
pipelineex0_.PARAMETER_VALUE as PARAMETER3_12_,
pipelineex0_.GUID_PIPELINE_TRACKING as GUID4_12_
from FCT_PIPELINE_EXEC_PARAMETER pipelineex0_
where pipelineex0_.GUID_PIPELINE_TRACKING = 'A5916E73CF1E406DA26F65C24BFBF694' /* :p0 */
and pipelineex0_.PARAMETER_NAME = 'lid' /* :p1 */)
where rownum <= 1 /* :p2 */
and second
select GUID1_12_,
PARAMETER2_12_,
PARAMETER3_12_,
GUID4_12_
from (select pipelineex0_.GUID_PIPELINE_EXEC_PARAMETER as GUID1_12_,
pipelineex0_.PARAMETER_NAME as PARAMETER2_12_,
pipelineex0_.PARAMETER_VALUE as PARAMETER3_12_,
pipelineex0_.GUID_PIPELINE_TRACKING as GUID4_12_
from FCT_PIPELINE_EXEC_PARAMETER pipelineex0_
where pipelineex0_.GUID_PIPELINE_TRACKING = 'A5916E73CF1E406DA26F65C24BFBF694' /* :p0 */
and pipelineex0_.PARAMETER_NAME = 'period' /* :p1 */)
where rownum <= 1 /* :p2 */
IMHO is this PARAMETER_NAME with 'lid' and 'period' that is generating different query plans.
thanks in advance
To generate the same plan each time the parameter needs to be set to the same length regardless of the parameter value.
You can customise the driver implementation to set the query parameter length to the field length specified in your mapping.
public class CustomOracleClientDriver : OracleClientDriver
{
protected override void InitializeParameter(IDbDataParameter dbParam, string name, SqlType sqlType)
{
base.InitializeParameter(dbParam, name, sqlType);
if (sqlType.LengthDefined)
dbParam.Size = sqlType.Length;
}
}
(NOTE: Inherit from OracleDataClientDriver if you are using ODP.Net)
If you are using Fluent NHibernate you register your driver implementation like this:
Fluently.Configure()
.Database(
OracleDataClientConfiguration.Oracle10
.ConnectionString(c => c.FromAppSetting("ConnectionString"))
.Driver<CustomOracleClientDriver>())
I tested this with an overridden OracleClientDriver (using the old Microsoft Oracle driver, not ODP.NET), similar to the code in the answer from mattk, and I didn't see any differences in the Oracle execution, although string parameters now had a common size.
Here's my post on Stackexchange DBA.
Oracle Enterprise Manager showed no duplicate queries for my NHibernate generated SQL, and in both versions, each call caused a parse (up to some 1000 for long testing), but very few hard parses, with no differences between variable and fixed parameter length.
In fact, Oracle created duplicate query plans only for queries without bind parameters, but with values concatenated into the SQL string (something to avoid in coded SQL). So it now seems to me that parameter size doesn't matter for Oracle, when it comes to reuse query plans (or, in Oracle terms, cursor sharing).
Oracle probably only compares the SQL string for plan matching, while SQL Server also checks the parameter definitions. You can also see a difference when looking at the dynamic SQL commands EXECUTE IMMEDIATE (Oracle) and sp_executesql (SQL Server): sp_executesql also gets a string with parameter definitions (in a string, not as parameters for the sp_executesql call itself!). I know NHibernate/ADO.NET uses sp_executesql when sending parameterized queries to SQL Server, so it likely has a different handling under SQL Server. Also, when connecting to SQL Server via NHibernate, all string parameters have unique sizes (from NHibernate mapping or default max length), so the problem has likely been fixed where relevant. Correct me if I'm wrong!
Using Prepare/prepare_sql in ADO.NET/NHibernate has some disadvantages: depending on implementation, before any SQL is executed, a Prepare request has to be sent to the database, the application has to keep a handle for the prepared statement, and it can be used only for one connection. Meaning: new handles must be created often. When I tested with Oracle and ODP.NET, it was somewhat slower than the non-prepared version, although querying by handle itself is (little) more performant than by parameterized SQL, matched on database by string equality. Likely, Prepare is good if the application uses many times the same query within the same DB connection or NHibernate session.

MS-Access ODBC Connection to Oracle for SQL

I'm trying to connect to an Oracle Database with Access 2003.
I want to use an ODBC connection and I want to set it up so that the user doesn't need to enter a password.
One of the problems I am having though is my sql query uses INTERFACE.Products and Access sees the period and thinks I'm trying to opening a file. IE Interface.MDB, when thats a part of my sql query.
Option Compare Database
Function OracleConnect() As Boolean
Dim ws As Workspace
Dim db As Database
Dim LConnect As String
Dim myQuery As String
Dim myRS As Recordset
On Error GoTo Err_Execute
LConnect = "ODBC;DSN=Oracle;UID=user;PWD=password;"
'Point to the current workspace
Set ws = DBEngine.Workspaces(0)
'Connect to Oracle
Set db = ws.OpenDatabase("", False, True, LConnect)
myQuery = "Select * from INTERFACE.Products"
Set rst = db.OpenRecordset(myQuery)
rst.Close
db.Close
Exit Function
Err_Execute:
MsgBox MsgBox("Error Number: " & Err.Number & " Message: " & Err.Description)
End Function
Could you try something like:
Set rst = db.OpenRecordset(mQuery, dbOpenSnapshot, dbSQLPassThrough)
This would just send the query verbatim to the Oracle database.
In the Access documentation for OpenRecordset you'll find this note:
If you open a Recordset in a Microsoft Access workspace and you don't specify a type, OpenRecordset creates a table-type Recordset.
This may be the source of the error you're getting.
As a rule, I would suggest to always be explicit in your parameters to OpenRecordset: the default behaviour isn't always consistent and it can generate strange errors.
I'm not sure about your design, but one way to make things a bit easier for you may be to simply create linked tables in Access.
In that case, you would be able to write queries against your INTERFACE.Products Oracle table as if if were a local Products table.
It would also save you from having to manage the connections yourself.
The recordset you are creating must use a query written with Access SQL. The table must be an Access table (a table defined in the mdb). It can be a local table or linked table. An Access table cannot have a "." (dot) in its name. If you were to link the Oracle table INTERFACE.Products in Access it would be INTERFACE_Products (Access will replace illegal characters in the remote database table name with "_") unless you renamed it in Access. Thus your query should be "Select * from INTERFACE_Products"
It is possible to create a recordset using Oracle SQL, but that must be created with a Pass-Through query.

Resources