I know this is a redundant question but could not find a similar scenario to mine.
I have a stored query which, when executed, picks values from the filters on an opened form and display results. All works.
Here is how the query is called:
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "qry"
Dim rsReport As New ADODB.Recordset
rsReport.CursorType = adOpenKeyset
rsReport.CursorLocation = adUseClient
rsReport.Open cmd
I am trying to use the same query from VBA to create Excel files which can be downloaded or emailed and I am getting a "Too few parameters" error now (while the form is still open). Can anyone set me in the right direction why this is happening please?
When executing a query using VBA, you can't reference open forms. You need to explicitly state all parameters.
If you're executing the query using DoCmd.RunQuery, you can set parameters using DoCmd.SetParameter.
If you're executing the query using QueryDef.Execute, you can set parameters using the QueryDef.Parameters collection.
If you're executing the query using an ADODB.Command, you can set parameters by appending parameters to the Command.Parameters collection, in the following way:
Command.Parameters.Append Command.CreateParameter ("MyParameterName", adInteger, adParamInput) where adInteger is the type. After that, you still need to set the parameter to a value by setting Command.Parameters("MyParameterName").Value = MyValue
Also see this question for info on ADODB parameters. They are a bit more tricky.
All parameters need to be filled in before executing the query.
Related
This may be a dumb question, but I can't find an answer.
I'm working in Classic ASP for the first time, trying to reformat the code to secure it against SQL injection. I've got the format and whatnot to make these changes (not the issue here), but one of the files is "generic". It gets the sproc name and parameters passed in and the SQL statement is constructed into a string. It's called by some 50+ other files. Since a SQL statement string was originally being used, and since it's been "genericized", there are no parameter names to put into my CreateParameter method. IS IT POSSIBLE TO CREATE A PARAMETER WITH NO NAME? (i.e. "#myParameterName") If Not, is there some other way to accoplish what I'm trying to do?
Set cnnSQLServer = Server.CreateObject("ADODB.Connection")
cnnSQLServer.Open Application("xyz_ConnectionString")
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = cnnSQLServer
cmd.CommandType = adCmdStoredProc
Set param1 = cmd.CreateParameter("#MyParameterName", adVarChar, adParamInput, 100, myValueToPass)
cmd.Parameters.Append(param1)
Set rstData = cmd.Execute
Thanks!
When using classic ADO with SQL Server OLEDB provider, ADODB.Command always passes parameters to stored procedures by position. Parameters names are ignored even if supplied, they are just used as keys in Command's internal Parameters collection. This is different in ADO.Net now.
<%
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
edit still recieving type mismatch error but updated code with help from comments
-updated code shown below
I am new to ASP and VBScript. I am trying to insert post data into a SQL database, however, I want to ensure the query is sterilized so I am trying to use a parameterized query. When I execute the query I get this error.
Microsoft VBScript runtime error '800a000d'
Type mismatch
my code looks like this
Set conn = Server.CreateObject("ADODB.Connection")
conn.Mode = 3
conn.open "Provider=SQLOLEDB;Data Source=xxx.xxx.xxx.xxx,xxxx;
database=db_example;uid=user;pwd=password;"
Dim oCmd
set oCmd= Server.CreateObject("ADODB.Command")
Dim sSQL
sSQL = "INSERT INTO tbl_Application (Expenses, Name, Why) VALUES (?, ?, ?);"
oCmd.CommandText = sSQL
oCmd.ActiveConnection= conn
Dim uPar
uPar = oCmd.CreateParameter("Expenses",200,1,255,session("Expenses"))
oCmd.Parameters.Append(uPar)
Dim vPar
vPar = oCmd.CreateParameter("Name",200,1,255,session("Name"))
oCmd.Parameters.Append(vPar)
Dim wPar
wPar = oCmd.CreateParameter("Why",200,1,255,session("Why"))
oCmd.Parameters.Append(wPar)
Dim oRS
oRS = oCmd.Execute()
I have tried typing the data as a varchar and a char. Please let me know if you have a fix for my problem. Also, I am intending to insert more than three pieces of data on the site - is there a better way than going through and making a parameter manually for each column?
The documentation for the CreateParameter method state that:
If you specify a variable-length data type in the Type argument, you
must either pass a Size argument or set the Size property of the
Parameter object before appending it to the Parameters collection;
otherwise, an error occurs.
129 corresponds to adChar, which is a variable-length data type and therefore requires you to pass a Size argument. Usually, you should use the defined length of the column in the database, but I have found that just using the length of the value I'm passing works also.
Just began using PostgresQL for a vb application (using visual studio 2005 pro) and connect via ODBC (there's a reason for using the ODBC connection and not the native PostgresQL connector) .
I'm used to using the #something and cmd.Parameters.Add("#something", data) format with MSSQL. I have 9 values i want to get from a form and use them in an insert statement but can't seem to figure the syntax for PostgresQL out.
Ideas? I've searched for two days trying to find an answer to this btw.
Edit: Sorry, I already deleted the code I was trying, but I kept getting "the column does not exist" error on column "name" which is my first paramater.
I know it's not a connection error or a naming convention issue or something like that because the following code does work. Here's how I'm doing it now for testing:
strSQL = "INSERT INTO tableb (name, extension, length,creationtime,lastaccesstime,lastwritetime,directoryname) VALUES ('Name','Extension','Length','CreationTime','LastAccessTime','LastWriteTime','DirectoryName')"
objConn.ConnectionString = strConnString
objConn.Open()
With objCmd
.Connection = objConn
.CommandText = strSQL
.CommandType = CommandType.Text
.ExecuteNonQuery()
End With
Oh, and the ODBC version I'm using is 8.03.02.00
More info:
The code causing the error:
strSQL = "INSERT INTO TABLEB (name) VALUES (#name)"
objConn.ConnectionString = strConnString
objConn.Open()
'Try
With objCmd
.Parameters.Add("#name", SqlDbType.Int)
.Parameters("#name").Value = "SomeText"
.Connection = objConn
.CommandText = strSQL
.CommandType = CommandType.Text
.ExecuteNonQuery()
End With
Code with parameters:
The exact error: ODBC Exception:
"ERROR [42703] ERROR: column "name" does not exist;
Error while executing the query"
The error occurs on the .ExecuteNonQuery
Thanks again!
problem is with below code
.Parameters.Add("#name", SqlDbType.Int)
.Parameters("#name").Value = "SomeText"
you set name as SqlDbType.Int but you set text value to it
give correct column type when declare the parameters and assign correct value which match with given data type.
And also give parameters as ? in sql statement and then add the command parameters in the same sequence given in the sql. ODBC do not support named parameters.
sample code :
strSQL = "INSERT INTO TABLEB (name) VALUES (?)"
objConn.ConnectionString = strConnString
objConn.Open()
With objCmd
.Parameters.AddWithValue("name", "SomeText")
.Connection = objConn
.CommandText = strSQL
.CommandType = CommandType.Text
.ExecuteNonQuery()
End With
As written that query is fine. Given the stated error, likely issues are:
Capitalization problems with quoting. If your column is defined as "Name" you need to refer to it as "Name" everywhere, not Name or name. See lexical structure.
Accessing the wrong database or an older version of the same database you set up and forgot, one that contains a tableb without a name column
In "anonymizing" the query you've hidden the real problem, or the error you quote doesn't match the code you're running. For example, quoting issues.
Are you sure the code you show is what you're getting the error from? You talk about #parameter etc in the text, but there's nothing like that in the code...
You should certainly be using parameterized queries instead of this approach to prevent SQL injection attacks, since I'm sure your real code doesn't hard code the values. Parameter use should be no different between psqlODBC and MS SQL Server's ODBC driver, that's half the point of query parameters. I don't speak Visual Basic (or ODBC if I can avoid it) and the SQL injection rosetta stone doesn't have details for VB.NET. Try doing what you do with MS SQL Server and if you have issues, follow up with a new question that includes the exact code and errors.
I have been tasked with creating an Excel spreadsheet as the front-end to a SQL database and some C# which performs a complicated calculation on the data. My boss wants the front end as a spreadsheet, and the calculation is seemingly too complex for VBA.
Presently, the stored procedure to retrieve the dataset works fine. The user will then edit the data in Excel and send it back to the database. The data needs to be inserted as a new set, preserving the records of the previous sets. I have therefore written the following SQL stored procedure to complete this task:
DECLARE #Now DATETIME = GETDATE()
DECLARE #DataSetID SMALLINT = (SELECT ISNULL(MAX([DataSetID]), 0) FROM [dbo].[tbl_DataSet]) + 1
--Add DataSet entry
INSERT INTO [dbo].[tbl_DataSet] ([DataSetID], [InputDate])
SELECT #DataSetID, #Now
--Add latest data
INSERT INTO [dbo].[tbl_Data] ([DataSetID], [DataID], [Amount])
SELECT #DataSetID, [DataID], [Amount]
FROM #DataTable
ORDER BY [DataID]
This means that the VBA code needs to be able to call the stored procedure, passing in a user-defined table type parameter, #DataTable AS [dbo].[typ_DataTable] READONLY.
My present method for calling a SQL user stored procedure, passing in standard datatype parameters:
Dim cnn As ADODB.Connection
Set cnn = New ADODB.Connection
cnn.ConnectionString = getConnectionString()
cnn.CursorLocation = adUseClient
cnn.Open
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
cmd.Parameters.Append cmd.CreateParameter("in", adInteger, adParamInput, , 1)
cmd.Parameters.Append cmd.CreateParameter("bReturn", adBoolean, adParamOutput)
cmd.ActiveConnection = cnn
cmd.CommandText = ProcedureName
cmd.CommandType = adCmdStoredProc
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset
Set rst = cmd.Execute()
Is there a simple way to pass in user-defined table types?
Alternatively, is there an entirely better system that I can use to do this?
The only work-arounds I can think of at the moment is to pass the data to C#, then send to the database using:
command.Parameters.Add(new SqlParameter {
ParameterName = "#DataTable",
Value = dataTable,
TypeName = "[dbo].[typ_DataTable]",
SqlDbType = SqlDbType.Structured });
However, this seems a little circuitous.
Thanks.
Update:
As there is already a pipeline from C# to Excel, it seems best to utilise this one nasty bit of code as opposed to introducing a second with Excel <-> SQL. C# can also be easily used to convert the data types and validate any parameters or return variables. Using pseudo-asynchronous methods with VBA to C# will also improve the Excel interface, preventing hanging on the UI thread.
ADO has a very nice table type.
"Presently, the stored procedure to retrieve the dataset works fine. "
Detach the dataset.
The user will then edit the data in Excel...
Append the new data to the detached dataset. Delete the old data from the detached dataset
... and send it back to the database.
Re-Attach the dataset. Update the data:
myRecordSet.ActiveConnection=oConnection
myRecordSet.UpdateBatch