How to make a parametrized SQL Query on Classic ASP? - sql

Can someone show me the simplest way of perform a parametrized SQL query using Classic ASP in VBscript?
A compilable example would be best.

Use the adodb.command object.
with createobject("adodb.command")
.activeConnection = application("connectionstring")
.commandText = "select * from sometable where id=?"
set rs = .execute( ,array(123))
end with
I would also advise to use a custom db access object instead of using adodb directly. This allows you to build a nicer api, improves testability and add hooks for debuging/logging/profiling. Secondly you can add request scoped transactions with implicit rollback's on errors using the class_terminiate event. Oure db access object offers the following query api
call db.execute("update some_table set column=? where id=?", array(value, id))
set rs = db.fetch_rs("select * from some_table where id=?", array(id))
count = db.fetch_scalar("select count(*) from some_table where column > ?", array(value))

I'm assuming you are referring to a parameterized SQL Query. If this is the case, then the VBScript code would look something like this:
Set adoCon = Server.CreateObject("ADODB.Connection")
adoCon.Open "connectionstring"
SET cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = adoCon
cmd.CommandType= adCmdStoredProc
cmd.CommandText = "GetCustomerByFirstName"
cmd.Parameters.Append cmd.CreateParameter("#FirstName",adVarchar,adParamInput,50,"John")
Set Rec = cmd.Execute()
While NOT Rec.EOF
'code to iterate through the recordset
Rec.MoveNext
End While
UPDATE: You need to include the ADOVBS.inc file for the constants to be recognized.
Here's a link: ADOVBS.inc

Another option to including adovbs.inc is to add a reference to the following type library near the top of your ASP. Supposedly this has better performance than an include:
<!--METADATA TYPE="TypeLib" NAME="ADODB Type Library" UUID="00000205-0000-0010-8000-00AA006D2EA4" FILE="C:\Program Files\Common Files\System\ado\msado15.dll" VERSION="2.5" -->
Here is a list of some type libraries.

Related

getting error while executing my select query - Arguments are of wrong type , are out of acceptable range

I am getting an error while executing a SQL select query in VBA.
Error : Arguments are of wrong type , are out of acceptable range ,or are in conflict in one another.Please find my code below
Public Function Get Invoices()
Dim objConn As Object
Dim objRecordSet As Object
Dim objRecCmd As Object
strQuery = "Select Distinct [RECHNR] from [Sheet1$]"
strFolderPath = "\test1"
Set objConn = CreateObject("ADODB.Connection")
Set objRecCmd = CreateObject("ADODB.Command")
objConn.Open ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strFolderPath & ";Extended Properties=""Excel 12.0;IMEX=1""")
Set objRecordSet = CreateObject("ADODB.Recordset")
objRecordSet.CursorLocation = adUseClient
objRecCmd.ActiveConnection = objConn
objRecCmd.CommandText = strQuery
objRecordSet.Open objRecCmd, , adOpenKeyset, adLockOptimistic
End Function
Error is coming in the line - objRecordSet.CursorLocation = adUseClient
Your code references the following:
adUseClient
adOpenKeyset
adLockOptimistic
These enums (short for enumerations - you might think of them as constants, or codewords for specific numbers - Google "What is an Enum" for further info) are not recognised by Excel, so it will assume that they are variables.
If you use Option Explicit in your code, Excel would tell you this.
Because these variables don't exist, Excel interprets them as 0. You will see below, 0 is not the value you want used in any of the cases. You need to use the correct number/code to get your script to request the right thing.
You have three options to do this:
Option 1
You could create variables named the same as the enums. You could put the following at the start of your code:
adUseClient = 3
adOpenKeyset = 1
adLockOptimistic = 3
This does work, but isn't recommended - as unlikely as it is, Microsoft could in theory change their coding and your script would start requesting very strange things until you realise and action a fix.
Option 2
You could replace your use of the enums with actual values. Swapping the enum name with the value above would solve the problem. But again, if Microsoft were to change things up in a later version of ADO, chaos might ensue.
Option 3
Reference the necessary library in your Project's References. Go to Tools > References and tick/select:
Microsoft ActiveX Data Objects Recordset x.x Library
In doing this, Excel becomes 'aware' of the real values - from the Library - and so should always use the correct one.

Setting NO COUNT ON in VBA ADODB

After hours of frustration I have realized that I have become a victim of Error Messaging Referencing #Temp Table with ADO-SQLOLEDB
When you try to create a ADO recordset based on a #Temp table created
within a stored procedure called by ADO using the SQLOLEDB provider,
you may encounter one of the following error messages...
The solution is to add SET NOCOUNT ON to either the Stored Procedures, or to the VBA code when the recordset is open. At the moment I am stuck with adding it to my VBA code.
The example code that MS gives as a solution is something like:
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
cmd.ActiveConnection = conn
cmd.CommandText = "spThatHasTempTable"
cmd.CommandType = adCmdStoredProc
..
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = conn
.Open "SET NOCOUNT ON"
End With
rs.Open cmd, , , , adCmdStoredProc
This works for me. However I feel like that setting SET NOCOUNT ON really should be a part of the Command object construction, but I can't seem to figure out how to do just that.
So how would I include SET NOCOUNT ON when constructing the Command object?
Can you combine the SQL? so your command becomes "set nocount on; select * from table"?
I know in some situations you can't have multiple commands.

Connection to SQL Database

I am Making connection to Sql Database and in one connection i am changing my query many
times,This is the basic structure of my code.The structure works fine.
I have Following Questions
1) Is this the right method of sending different Query to Sql Database.
2) Will this cause any memory leaks or error.
dim conn
set conn=CreateObject("ADODB.Connection")
With conn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString ="Data Source=C:\MAP_sample.xls;" & "Extended Properties=Excel 8.0;"
.Open
End With
Set rs = CreateObject("ADODB.Recordset")
Query = "SELECT * FROM [Sheet1$] where StateName='ABC'"
rs.Open Query,conn
rs.close
CHANGE THE QUERY
Query = "SELECT * FROM [Sheet1$] where ID='321'"
rs.Open Query,conn
rs.close
change the query
Query = "SELECT * FROM [Sheet1$] where NAME='SMITH'"
rs.Open Query,conn
rs.close
Set rs = nothing
conn.close
Set conn = nothing
looks like classic ASP with visual basic.
It is one of the possible variants to send queries, it is not bad, a little bit boilerplate, as suggestion - try to re-factor it into separate class or functions
No, it will no cause memory leaks or errors as you're calling close
You can later call the Open method to re-establish the connection to the same, or another, data source.
the only problem with this approach comes when you need to have 2 recordset opened at the same time
if you speak about general code readability - it is better to use meaningful variable names

How to use ASP variables in SQL statement

<%
postit = request.querystring("thispost")
response.write(postit)
%>
postit is the variable. The response.write works and this is all above the SQL statement below.
This is the SQL however when I add the postit variable I get this error message:
delCmd.CommandText="DELETE * FROM post WHERE (pos_ID = postit )"
Microsoft Access Database Engine error '80040e10'
No value given for one or more required parameters.
/student/s0190204/wip/deleterecord.asp, line 32
Add a parameter to the SQL:
delCmd.CommandText="DELETE * FROM post WHERE (pos_ID = ?)"
delCmd.Parameters.Append delCmd.CreateParameter("posid", adInteger, adParamInput) ' input parameter
delCmd.Parameters("posid").Value = postit
Couple of things that will help you in the future
Use Option Explicit to avoid hiding issues that will come back to bite you later on
Use ADODB.Command object, which is very versatile enabling to do a range of database calls, from simple dynamic SQL statements to Stored Procedures without the risk of SQL injection.
There are a few tips that can speed things up when using the ADODB.Command object in your code which will be demonstrated in the example below (assumes you already have a connection string stored in a global config call gs_connstr);
<%
Option Explicit
Dim postit
postit = Request.QueryString("thispost")
'Always do some basic validation of your Request variables
If Len(postit) > 0 And IsNumeric(postit) Then CLng(postit) Else postit = 0
Dim o_cmd, o_rs, a_rs, i_row, i_rows, l_affected
Dim SQL
'SQL statement to be executed. For CommandType adCmdText this can be any dynamic
'statement, but adCmdText also gives you an added bonus - Parameterised Queries
'instead of concatenating values into your SQL you can specify placeholders (?)
'that you will define values for that will get passed to the provider in the order
'they are defined in the SQL statement.
SQL = "DELETE * FROM post WHERE (pos_ID = ?)"
Set o_cmd = Server.CreateObject("ADODB.Command")
With o_cmd
'ActiveConnection will accept a Connection String so there is no need
'to instantiate a separate ADODB.Connection object the ADODB.Command object
'will handle this and also open the connection ready.
.ActiveConnection = gs_connstr
.CommandType = adCmdText
.CommandText = SQL
'When using Parameters the most important thing to remember is the order you
'appended your parameters to the Parameters collection as this will determine
'the order in which they are applied to your SQL query at execution. Because
'of this the name you give to your parameters is not important in terms of
'execution but I find specifying a meaningful name is best (especially when
'revisiting some code a few years down the line).
Call .Parameters.Append(.CreateParameter("#pos_ID", adInteger, adParamInput, 4))
'Parameter values can be passed in via the Execute() method using an Array
'without having to define the parameter values explicitly. You can also specify
'the records affected value to return number of rows affected by a DELETE,
'INSERT or UPDATE statement.
.Execute(l_affected, Array(postit))
End With
'Always tidy up after yourself, by releasing your object from memory, this will
'also tidy up your connection as it was created by the ADODB.Command object.
Set o_cmd = Nothing
%>
Try this code:
<% Dim postit, stringSQL, objectCon
postit = request.querystring("thispost")
Set objectCon = Server.CreateObject("ADODB.Connection")
objectCon.ConnectionString "Driver={SQL SERVER};Server=server_name;UID=user_name;PWD=password;Database=database_name" 'SET CONNECTION STRING OF YOUR DATABASE
stringSQL = "DELETE FROM post WHERE pos_id='" & postit & "'"
objectCon.Open
objectCon.Execute(stringSQL)
objectCon.Close() %>
You're not passing the value of postit to Access; instead, you're telling Access to find & use a variable called postit. Of course, said variable doesn't exist in Access -- it only exists in your code. The fix is just a couple of quote marks and a pair of ampersands.
delCmd.CommandText="DELETE * FROM post WHERE (pos_ID = " & postit & " )"
(Naturally, you should validate postit before you go sending it off to your database. A simple CDbl() can do the trick, assuming it's a numeric value.)
Here I'm trying to get the car_color of the car using the id of the car.
Now I can use the car_color record set in my code.
I would also recommend using CLng when passing in values, it'll prevent sql injections.
If the carID is not a number you'll get the following error:
"500 response from the server. Remember to open and close the sql connection."
Here is the code:
sql = "Select * from Cars Where ID = " & clng(carID)
rs.open
if not rs.eof then
carID = rs("car_ID")
carColor = rs("car_color")
end if
rs.close
More easy for delete, this way is useful when not need to check the recordset:
cn.open "yourconnectionstring"
cn.execute "DELETE * FROM post WHERE pos_ID = " & request.querystring("thispost")
cn.close

Execute Query from Access via Excel Query in VBA

Access has saved a query that was designed with the query builder called 'myQuery'. The database is connected to the system via ODBC connection. Macros are all enabled.
Excel Has makes a ADODB connection to connect to the database via
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
With con
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Open "MyDatabase.accdb"
End With
Usually you would go ahead and just write your SQL, which is perfectly fine and then just do something like
Dim sqlQuery As String
sqlQuery = "SELECT * FROM myTable"
Set rs = New ADODB.Recordset
rs.Open sqlQuery, con, ...
But I want to access the query that I saved in the access database. So how do I call the saved query in the database that I just connected.
Tried already
con.Execute("EXEC myQuery") but that one told me it could not be find myQuery.
rs.Open "myQuery", con but that one is invalid and wants SELECT/etc statements from it
I think you can treat it like a stored procedure.
If we start right before Dim sqlQuery As String
Dim cmd as new ADODB.Command
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "myQuery"
cmd.ActiveConnection = con
Set rs = cmd.Execute()
Then pickup your recordset work after this.
You were nearly there:
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
With con
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Open "z:\docs\MyDatabase.accdb"
End With
con.Execute "MyQuery"
Just leave out Exec.
You can add parameters, too, this is a little old, but should help: update 2 fields in Access database with Excel data and probably a Macro
I was able to run an update query that was already saved in Access using:
Connection.Execute "My_Update_Query_Already_Saved_In_Access", adExecuteNoRecords, adCmdStoredProc
This gave me errors until I replaced spaces in the query name with underscores in both the Access database and the execute statement.
This is sort of a hack job, but you can query a query. That is, replace your sql string with the following:
sqlQuery = "SELECT * FROM QueryName;"
Before running this, one must ensure that the Access Database has been saved ie. press Ctrl+S (it is not sufficient that the query was run in Access).
Long time since this thread was created. If I understand it correctly, I might have something useful to add. I've given a name to what the OP describes, that being the process of using SQL from a query saved in an ACCDB to run in VBA via DAO or ADOBD. The name I've given it is "Object Property Provider", even with the acronym OPP in my notes, and for the object name prefix/suffix.
The idea is an existing object in an ACCDB (usually a query) provides a property (usually SQL) that you need to use in VBA. I slapped together a function just to suck SQL out of queries for this; see below. Forewarning: sorry, but this is all in DAO, I don't have much use for ADODB. Hope you will still find the ideas useful.
I even went so far as to devise a method of using/inserting replaceable parameters in the SQL that comes from these OPP queries. Then I use VBA.Replace() to do the replacing before I use the SQL in VBA.
The DAO object path to the SQL of a query in an ACCDB is as follows:
mySqlStatement = Access.Application.CurrentDb.QueryDefs("myQueryName").SQL
The way I use replaceable parameters is by evaluating what needs to be replaced, and choosing an unusual name for the paramater that cannot possibly exist in the real database. For the most part, the only replacements I've made are field or table names, or the expressions of WHERE and HAVING clauses. So I name them things like "{ReplaceMe00000001}" and then use the Replace() function to do the work...
sqlText = VBA.Replace(sqlText, "{ReplaceMe00000001}", "SomeActualParameter")
...and then use the sqlText in VBA. Here's a working example:
Public Function MySqlThing()
Dim sqlText as String
Dim myParamater as String
Dim myExpression as String
'Set everything up.
sqlText = getSqlTextFromQuery("myQuery")
myParameter = "{ReplaceMe00000001}"
myExpression = "SomeDateOrSomething12/31/2017"
'Do the replacement.
sqlText = VBA.Replace(sqlText, myParameter, myExpression)
'Then use the SQL.
db.Execute sqlText, dbFailOnError
End Function
Function getSqlTextFromQuery(ByVal oppName As String) As String
Dim app As Access.Application
Dim db As DAO.Database
Dim qdefs As DAO.QueryDefs
Dim qdef As DAO.QueryDef
Dim sqlText As String
Set app = Access.Application
Set db = app.CurrentDb
Set qdefs = db.QueryDefs
Set qdef = qdefs(oppName)
oppGetSqlText = qdef.SQL
End Function