I'm trying to UPDATE a table column named "OrigMask" in myfile.sqlite by using an array created in my VBA code. I'm struggling with the syntax. The VBA array is named "NewMask" and has 864 elements as does the table "OrigMask". How do I create the sql statement in VBA and execute. Help is VERY much appreciated !
I'm establishing a statement & connection like so:
Set conn = CreateObject("ADODB.Connection")
Set rst = CreateObject("ADODB.Recordset")
conn.Open "DRIVER=SQLite3 ODBC Driver;Database=C:\myfile.sqlite;"
strSQL1 = "UPDATE MyTable SET OrigMask= NewMask;"
rst.Open strSQL1, conn
Set rst = Nothing: Set conn = Nothing
Several issues here:
You need to iterate through you array and not integrate whole object in SQL as NewMask is unrecognized identifier in database.
An update query is an action query and not a resultset to be retrieved in a recordset.
You need a WHERE clause in UPDATE or all records are update not row by row.
Therefore, consider a For Each loop through a parameterized ADO command object:
strSQL1 = "UPDATE MyTable SET OrigMask = ? WHERE id = ?;"
i = 1
For Each var In NewMask
Set cmd = CreateObject("ADODB.Command")
With cmd
.ActiveConnection = conn
.CommandText = strSQL1
.CommandType = adCmdText
.CommandTimeout = 15
.Parameters.Append .CreateParameter("maskparam", adVarChar, adParamInput, 255, var)
.Parameters.Append .CreateParameter("idparam", adInt, adParamInput, , i)
.Execute
End With
i = i + 1
Next var
Related
I'm trying to pass a list of parameters to a SQL query in VBA, I have an excel table with a list of codes in a range that is dynamic, could 2, could be 2000. I want to then return all data from SQL where the codes match. Below I'm trying to define an ADODB parameter as an array, then I want to pass that to SQL.
However defining the parameter variable as a range gives a type mismatch. Or Object required if I don't define as an array;
Dim conn As New ADODB.Connection
conn.Open "Provider=SQLOLEDB; Data Source=XXXXXXXXX; Initial Catalog=CDB; Integrated Security=SSPI;"
Dim code(0) As ADODB.Parameter
Set code(0) = wb.ActiveSheet.Range(Cells(2, colCode), Cells(rowCount, colCode)).Value
'Dim code As ADODB.Parameter
'Set code = wb.ActiveSheet.Range(Cells(2, colCode), Cells(rowCount, colCode)).Value
'Dim code(rowCount - 1) As ADODB.Parameter
'Set code(rowCount - 1) = wb.ActiveSheet.Range(Cells(2, colCode), Cells(rowCount, colCode)).Value
Set cmd = New ADODB.Command
Set rs = New ADODB.Recordset
With cmd
.ActiveConnection = conn
.CommandType = adCmdText
.Parameters.Append .CreateParameter("#code", adVarChar, adParamInput, 10000, code)
.CommandTimeout = 0
End With
sql = "SELECT * FROM table WHERE code in (?);"
cmd.CommandText = sql
Set rs = cmd.Execute
OK, I'm missing something obvious here - I have an SP that takes in an integer ID and returns a string. I've used this SP for quite a while with DAO. Now I need to switch to ADO so I can run it under and existing connection (another question I'll post elsewhere).
So my code follows. It returns no errors but it also returns no results. The output parameter is null. What am I missing?
Dim adoCon As ADODB.Connection
Dim adoCMD As ADODB.Command
Dim SQLstr As String
Dim ConStr As String
'--- get connection string from existing object, but strip leading odbc; piece
ConStr = Replace(CurrentDb.TableDefs("[TableName]").Connect, "ODBC;", "")
Set adoCon = New ADODB.Connection
adoCon.ConnectionString = ConStr
adoCon.Open
Set adoCMD = New ADODB.Command
With adoCMD
.ActiveConnection = adoCon
.CommandType = adCmdStoredProc
.Parameters.Append .CreateParameter(, adInteger, adParamReturnValue, , Null) ' return value
.Parameters.Append .CreateParameter("Path", adVarChar, adParamOutput, 500, Null)
.Parameters.Append .CreateParameter("AsyID", adInteger, adParamInput)
.Parameters.Item("AsyID").Value = AsyID
.CommandText = "dbo.spGetAncestry"
.Execute
End With
GetHeritage = adoCMD.Parameters(1).Value 'parm(0) = 0; parm(1) = NULL; parm(2) = AsyID
adoCon.Close
Although your code should work. Please remove the optional expressions in the parameter definition and try the following:
Dim rv as Integer
Set adoCMD = New ADODB.Command
With adoCMD
.ActiveConnection = adoCon
.CommandType = adCmdStoredProc
.Parameters.Append .CreateParameter("RETURN_VALUE", adInteger, adParamReturnValue)
.Parameters.Append .CreateParameter("Path", adVarChar, adParamOutput, 500)
.Parameters.Append .CreateParameter("AsyID", adInteger, adParamInput, , AsyID)
.CommandText = "dbo.spGetAncestry"
.Execute
End With
rv = adoCMD.Parameters("RETURN_VALUE").Value
GetHeritage = adoCMD.Parameters("Path").Value
also make sure your SP is returning the correct data type and size for your output parameter and adjust the code accordingly. If you're returning VARCHAR(MAX), then that is treated as a "BLOB" in ADO, see this related question What are the limits for ADO data types?.
In this case you can try returning varchar(8000) from the SP and updating the code accordingly.
Found it.
Apparently, in the ADO call, it doesn't matter what you set the return value to (I was trying to use "" or even " " before I set it to null) when it executes in SQL batch, it is simply set to NULL as it shows in this trace form. For this run, the Output was initialized as " ", but the batch passed in NULL.
[!SQL trace of the above query being executed with " " in the initialization of the Output variable1]1
Normally, a null wouldn't be a problem as the typical SP assignment would be:
SET #Path = [SELECT value from table]
or, if it was a concatenation, you would initialize the variable:
SET #Path = ''
before stringing together the input.
In this particular case, though, the SP is recursive. It calls itself passing an input and the output values to the new copy. Because of this, you can't initialize the value and you can't use a straight assignment. To get around this, I needed to use:
#path = COALESCE(#path, '') + [SELECT value from table]
to trap any NULL passed in.
I need to insert some data into DB, there is a problem..it gives me an error :
Source line:
SET sql ="Insert Into Products (ProductName,SupID,CatID,Price,Pic,Description) Values( '"&pName&"','"&pbId&"','"&pcId&"','"&price&"','"&pic&"','"&desc&"')"
Description: Object required: '[string: "Insert Into Products"]'
I dont understand what he wants..
This is my code:
dim sql
dim price
dim desc
dim pName
dim pcId
dim pbId
dim pic
set pic = Request.Form("picUpload")
set desc = Request.Form("tbDescProduct")
set price= Request.Form("tbPriceProduct")
set pcId =Request.Form("ddlCategoryForProd")
set pbId =Request.Form("ddlBrandForProd")
set pName=Request.Form("tbProductName")
IF((bName<>"")AND(desc<>"")AND(price<>"")AND(pcId<>"-1")AND(pbId<>"-1")AND (pic<>"")) THEN
set con = Server.CreateObject("ADODB.Connection")
con.open "DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=" & Server.MapPath("WebData/DB.mdb") & ";"
set rs = con.Execute("Select * FROM Products WHERE ProductName = '"&pName&"' and mode= true")
IF rs.EOF = true then
SET sql ="Insert Into Products (ProductName,SupID,CatID,Price,Pic,Description) Values( '"&pName&"','"&pbId&"','"&pcId&"','"&price&"','"&pic&"','"&desc&"')"
SET rs =con.Execute(sql)
response.write("<script language=""javascript"">alert('Product added succesfully!');</script>")
ELSE
response.write("<script language=""javascript"">alert('Product already exist!');</script>")
END IF
'END IF
In VBScript, VBA and VB5/6, SET is required to assign an object reference; to assign any other sort of data (including a string), just remove it:
sql = "Insert Into Products (ProductName,SupID,CatID,Price,Pic,Description) Values( '"&pName&"','"&pbId&"','"&pcId&"','"&price&"','"&pic&"','"&desc&"')"
(In VBA and VB5/6 you could also use LET here.)
The reason SET works when assigning the result of a Request.Form("foo") call is because the Form collection is a collection of objects - the subsequent tests against "" and "-1" are valid only because the objects returned have a default parameterless property or method that return a string-compatible variant.
If I was to guess I'd say your problem is you're passing the SupID and CatID fields as strings when they are probably integers. The problem with handling INSERT this way is you leave yourself open to SQL Injection plus you encounter data type issues like you seem to be experiencing here.
Whenever possible when interacting with a database you should try to use Parameterised Queries. In Classic ASP the best object to do this is ADODB.Command.
Here is an example using your code;
NOTE: If you have problems with the ADO named constants like adParamInput then look in the links section below to see how to use the METADATA tag in your global.asa file to reference the ADO type library across your application.
Dim cmd, sql, conn_string, rs, data
'Wouldn't recommend storing your database inside your website root, instead
'store it outside in another folder and set up a variable in an include file
'to store the location. That way it is not accessible to everyone.
conn_string = "DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=" & Server.MapPath("WebData/DB.mdb") & ";"
Set cmd = Server.CreateObject("ADODB.Command")
sql = "SELECT * FROM Products WHERE ProductName = ?"
With cmd
.ActiveConnection = conn_string
.CommandType = adCmdText
.CommandText = sql
Call .Parameters.Append(.CreateParameter("#ProductName", adVarWChar, adParamInput, 50))
Set rs = .Execute(, Array(pName))
If Not rs.EOF Then data = rs.GetRows()
Call rs.Close()
Set rs = Nothing
End With
If IsArray(data) Then
sql = ""
sql = sql & "INSERT INTO Products (ProductName, SupID, CatID, Price, Pic, Description) " & vbCrLf
sql = sql & "VALUES (?, ?, ?, ?, ?, ?)"
Set cmd = Server.CreateObject("ADODB.Command")
With cmd
.ActiveConnection = conn_string
.CommandType = adCmdText
.CommandText = sql
'Define Parameters
'Making some assumptions about your data types, but you can modify these to fit
'good guide for this is http://www.carlprothman.net/Technology/DataTypeMapping/tabid/97/Default.aspx
Call .Parameters.Append(.CreateParameter("#ProductName", adVarWChar, adParamInput, 50))
Call .Parameters.Append(.CreateParameter("#SupID", adInteger, adParamInput, 4))
Call .Parameters.Append(.CreateParameter("#CatID", adInteger, adParamInput, 4))
Call .Parameters.Append(.CreateParameter("#Price", adCurrency, adParamInput, 4))
Call .Parameters.Append(.CreateParameter("#Pic", adVarWChar, adParamInput, 255))
Call .Parameters.Append(.CreateParameter("#Description", adLongVarWChar, adParamInput, 1000))
'Some of your variables may require conversion before setting the parameter values.
.Parameters("#ProductName").Value = pName
.Parameters("#SupID").Value = CLng(pbId)
.Parameters("#CatID").Value = CLng(pcId)
.Parameters("#Price").Value = price
.Parameters("#Pic").Value = pic
.Parameters("#Description").Value = desc
'Execute Command
.Execute()
End With
Set cmd = Nothing
Call Response.write("<script language=""javascript"">alert('Product added succesfully!');</script>")
Else
Call Response.Write("<script language=""javascript"">alert('Product already exist!');</script>")
End If
Links
Data Type Mapping
Using METADATA to Import DLL Constants
Answer from SQL insert into database with apostrophe
I tried to solve this all day long but it doesn't seem to work for me. I would like to execute a command and get the result back to a recordset.
The problem is one of two things: either I'm getting an empty response or there is a problem with my code. I know for sure that this command should fetch few lines from the DB. I added response.write inside the loop, but they are never printed.
Here is the code:
Set conn = Server.CreateObject("ADODB.Connection")
conn.open "PROVIDER=SQLOLEDB;DATA SOURCE=X;DATABASE=Y;UID=Z;PWD=W;"
Set objCommandSec = CreateObject("ADODB.Command")
With objCommandSec
Set .ActiveConnection = Conn
.CommandType = 4
.CommandText = "usp_Targets_DataEntry_Display"
.Parameters.Append .CreateParameter("#userinumber ", 200, 1, 10, inumber)
.Parameters.Append .CreateParameter("#group ", 200, 1, 50, "ISM")
.Parameters.Append .CreateParameter("#groupvalue", 200, 1, 50, ismID)
.Parameters.Append .CreateParameter("#targettypeparam ", 200, 1, 50, targetType)
End With
set rs = Server.CreateObject("ADODB.RecordSet")
rs = objCommandSec.Execute
while not rs.eof
response.write (1)
response.write (rs("1_Q1"))
rs.MoveNext
wend
response.write (2)
EDITED
After revising the code, following #Joel Coehoorn answer, the solution is:
set rs = Server.CreateObject("ADODB.RecordSet")
rs.oppen objCommandSec
instead of...
set rs = Server.CreateObject("ADODB.RecordSet")
rs = objCommandSec.Execute
Couple of tips after working with asp-classic for years
There is no need to create a ADODB.Connection you can pass a connection string direct to .ActiveConnection property of the ADODB.Command object. This has two benefits, you don't have instantiate and open another object and because the context is tied to the ADODB.Command it will be released with Set objCommandSec = Nothing.
A common reason for .Execute returning a closed recordset is due to SET NOCOUNT ON not being set in your SQL Stored Procedure, as an INSERT or UPDATE will generate a records affected count and closed recordset. Setting SET NOCOUNT ON will stop these outputs and only your expected recordset will be returned.
Using ADODB.Recordset to cycle through your data is overkill unless you need to move backwards and forwards through and support some of the more lesser used methods that are not needed for standard functions like displaying a recordset to screen. Instead try using an Array.
Const adParamInput = 1
Const adVarChar = 200
Dim conn_string, row, rows, ary_data
conn_string = "PROVIDER=SQLOLEDB;DATA SOURCE=X;DATABASE=Y;UID=Z;PWD=W;"
Set objCommandSec = CreateObject("ADODB.Command")
With objCommandSec
.ActiveConnection = conn_string
.CommandType = 4
.CommandText = "usp_Targets_DataEntry_Display"
.Parameters.Append .CreateParameter("#userinumber", adVarChar, adParamInput, 10, inumber)
.Parameters.Append .CreateParameter("#group", adVarChar, adParamInput, 50, "ISM")
.Parameters.Append .CreateParameter("#groupvalue", adVarChar, adParamInput, 50, ismID)
.Parameters.Append .CreateParameter("#targettypeparam", adVarChar, adParamInput, 50, targetType)
Set rs = .Execute()
If Not rs.EOF Then ary_data = rs.GetRows()
Call rs.Close()
Set rs = Nothing
End With
Set objCommandSec = Nothing
'Command and Recordset no longer needed as ary_data contains our data.
If IsArray(ary_data) Then
' Iterate through array
rows = UBound(ary_data, 2)
For row = 0 to rows
' Return our row data
' Row N column 2 (index starts from 0)
Call Response.Write(ary_data(1, row) & "")
Next
Else
' Nothing returned
Call Response.Write("No data returned")
End If
Looked at this for a few minutes, and it's been a long time since I've worked with classic asp, but I did see three things to look at:
Do you need to Open the connection before calling objCommandSec.Execute?
Can you try writing out a string literal inside the loop, that does not depend at all on the recordset... only that you are in fact looping through the code, so see if records are coming back to the recordset.
Have you checked the html source, to see if perhaps malformed html is hiding your results? I remember this happening a few times with tables in classic asp loops, where data would be hidden somehow between two rows, or a closing table tag in the wrong place would end the table, and later rows would not be visible.
Although this might not answer OPs question directly, it might help someone else looking for a solution.
recently I had a maintenance job that required me to modify something in a running ASP classic code (which I haven't write in ages). Procedure calls were written the same way as OP did and that wasn't how I did it in the past.
Here is the syntax I used in the past and I think it is a little more clean than other solutions provided here.
The following code shows how to read an output parameter, pass parameters to stored procedure, pass null value to parameter, read record count, and iterate in RecordSet.
dim conn, cmd, rs
set conn = Server.CreateObject("ADODB.Connection")
conn.Open "Driver={SQL Server};Server=servername;Uid=username;Pwd=password;Database=dbname;"
set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = conn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "procedurename"
cmd.Parameters.Refresh
cmd.Parameters("#nullparam") = null
cmd.Parameters("#strparam") = "1"
cmd.Parameters("#numparam") = 100
set rs = Server.CreateObject ("ADODB.RecordSet")
rs.CursorLocation = adUseClient ' to read recordcount'
rs.open cmd, , adOpenStatic, adLockReadOnly
Response.Write "Return Value: " & cmd.Parameters("#RETURN_VALUE") & "<br />"
Response.Write "Record count: " & rs.RecordCount & "<br />"
while not rs.EOF
' or do whatever you like with data'
Response.Write rs("colname") & "<br>"
rs.MoveNext
wend
I have excel VBA script:
Set cоnn = CreateObject("ADODB.Connection")
conn.Open "report"
Set rs = conn.Execute("select * from table" )
Script work fine, but i want to add parameter to it. For example " where (parentid = myparam)", where myparam setted outside query string. How can i do it?
Of course i can modify query string, but i think it not very wise.
You need to use an ADODB.Command object that you can add parameters to. Here's basically what that looks like
Sub adotest()
Dim Cn As ADODB.Connection
Dim Cm As ADODB.Command
Dim Pm As ADODB.Parameter
Dim Rs as ADODB.Recordset
Set Cn = New ADODB.Connection
Cn.Open "mystring"
Set Cm = New ADODB.Command
With Cm
.ActiveConnection = Cn
.CommandText = "SELECT * FROM table WHERE parentid=?;"
.CommandType = adCmdText
Set Pm = .CreateParameter("parentid", adNumeric, adParamInput)
Pm.Value = 1
.Parameters.Append Pm
Set Rs = .Execute
End With
End Sub
The question mark in the CommandText is the placeholder for the parameter. I believe, but I'm not positive, that the order you Append parameters must match the order of the questions marks (when you have more than one). Don't be fooled that the parameter is named "parentid" because I don't think ADO cares about the name other than for identification.
Alternative example returning a command from a function:
Function BuildCommand(conn As ADODB.Connection) As ADODB.Command
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
cmd.Parameters.Append cmd.CreateParameter("#name", adVarChar, adParamInput, 255, "Dave")
cmd.CommandText = "SELECT * FROM users WHERE name = #name;"
Set BuildCommand = cmd
End Function
A couple things to note:
When using adVarChar data type, the size argument to cmd.CreateParameter (e.g. 255) is required. Not supplying it results a run-time error 3708: Application-defined or object-defined error, as indicated in the documentation:
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.
If the cmd.ActiveConnection property is set when cmd.CommandText is set, and cmd.CommandText contains named parameters, cmd.Parameters will be populated accordingly. Calling cmd.Parameters.Append afterwards could result in duplicates. For example:
cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
Debug.Print cmd.Parameters.Count ' 0
cmd.CommandText = "SELECT * FROM users WHERE name = #name;"
Debug.Print cmd.Parameters.Count ' 1
cmd.Parameters.Append cmd.CreateParameter("#name", adVarChar, adParamInput, 255, "Dave")
Debug.Print cmd.Parameters.Count ' 2
I believe this is what is meant in the documentation, which is slightly inaccurate:
If the Prepared property of the Command object is set to True and the Command object is bound to an open connection when you set the CommandText property, ADO prepares the query (that is, a compiled form of the query that is stored by the provider) when you call the Execute or Open methods.
As a workaround, either set cmd.CommandText or cmd.ActiveConnection after adding parameters.