Hi I am fairly new to MySQL and have tried to follow examples to use parameter queries.
if I simply put a ? in the sql statement the code works, but I want to know how to pass multiple
parms so am trying to use a named parameter.
however I get an error
[MySQL][ODBC 5.1 Driver][mysqld-5.1.73-log]Unknown column '56case_id' in 'where clause'
/t3.asp, line 32
you will see commented out several other methods I have tried without success
and help would be great
my code is...
Set connContent = Server.CreateObject("ADODB.Connection")
connContent.ConnectionString=.....
connContent.Open
Set cmdContent = Server.CreateObject("ADODB.Command")
Set cmdContent.ActiveConnection = connContent
Set rs = Server.CreateObject("ADODB.Recordset")
cmdContent.Prepared = True
Const ad_varChar = 200
Const ad_ParamInput = 1
Const ad_Integer = 3
Const ad_DBDate = 133
Const ad_DBTimeStamp = 135
'theNumber = 23
'theText = "Hello there!"
'theDate = "2011-10-15"
case_id=56
SQL = " select * from tbl_cases where case_id > ?case_id; "
Set newParameter = cmdContent.CreateParameter("?case_id", ad_Integer, ad_ParamInput, 50, case_id)
cmdContent.Parameters.Append newParameter
'cmdContent.Parameters.Add(new MySqlParameter("case_id",case_id));
'cmdContent.Parameters.AddWithValue ("#Case_id", 3);
cmdContent.CommandText = SQL
set rs=cmdContent.Execute
do until rs.eof
response.write rs.fields("case_id")
rs.movenext
loop
%>
Named parameters aren't possible but multiple ? parameters work fine. The ?s are used in the order they are created so it really pays to keep the code well organized. Here is a no frills example... you'll see that I put the cmd statement first, immediately followed by the parameters in the order they are needed -- each of which is condensed into one line of code.
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open(connectionString)
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = conn
cmd.CommandText = "UPDATE metadata SET meta_key=?, meta_value=? WHERE meta_id=?;"
cmd.Parameters.Append cmd.CreateParameter("#meta_key", adVarChar, adParamInput, 255, meta_key)
cmd.Parameters.Append cmd.CreateParameter("#meta_value", adLongVarChar, adParamInput, len(meta_value), meta_value)
cmd.Parameters.Append cmd.CreateParameter("#meta_id", adInteger, adParamInput, 11, meta_id)
cmd.Execute rCount
response.write(rCount & " records affected")
conn.Close
it is not posible to use named Parameters with classic asp/adodb
Related
I'm trying to query a MySQL database from VBA in Excel by means of a stored procedure with some parameters. My problem is that I only get a maximum of one row in my (ADO) resultset. After hours of searching and troubleshooting I have given up. I've done a lot of research online (of course also on SO as there seem to be a lot of related issues, but none of which is mine - as far as I have seen). I can already inform you what does / does not work:
I see a lot about DAO ResultSets where the rs.moveLast has not been called. However, I'm using an ADO resultset, so this is not the problem.
When I use a SELECT * FROM some_table, everything works fine. It is specifically the StoredProcedure call that gives me headaches. :)
the parameters passed are valid. When I change them, I do see a different (and correct) result, though just one record. (the SP also check the validity and will change them when empty). I have even implemented special Debug stored procedures and a logging table in the database to log the passed parameters and the way they are handled. All is fine there.
when I run the same SP manually with the same parameters, I get 3 records.
I have changed the CursorLocation via the ADODB.Connection object to adUseClient and I've tried different means of retreiving the resultset (via the ADODB.ResultSet.Open method, via the ADODB.Connection object, via the PreparedStatement). The result stays the same. The CursorType is adOpenStatic (With a different CursorType, the recordCount would give me -1).
I have checked for multiple result sets (there shouldn't be any) and there are none extra.
In an attempt to provide a Minimal Reproducible Example I've created a new sample Stored Procedure with some testdata, which gives me exactly the same results (i.e. only one row of the table that I would get running the query in HeidySQL or MySQL Workbench):
for instance:
When I enter "S8" in B2 (without the quotes), I should expect three rows - on a new sheet in the same workbook (in line with the data set given below, the result should be: {{S8, apple, red}, {S8, grapes, white}, {S8, banana, yellow}}). Instead, I only get {S8, grapes, white} - strangely enough, this would be the middle record in both SQL clients?
Option Explicit
Enum enums 'necessary because of late binding
adUseClient = 3 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/cursorlocationenum?view=sqlallproducts-allversions
adCmdStoredProc = 4 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/commandtypeenum?view=sqlallproducts-allversions
adVarChar = 200 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/datatypeenum?view=sqlallproducts-allversions
adParamInput = 1 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/parameterdirectionenum?view=sqlallproducts-allversions
End Enum
Sub excelmysql()
Dim conn As Object
Dim spCommand As Object
Dim param1 As Object
Dim rs As Object
Set conn = CreateObject("ADODB.Connection")
Set spCommand = CreateObject("ADODB.Command")
Set param1 = CreateObject("ADODB.Parameter")
Set rs = CreateObject("ADODB.Recordset")
' conn.CursorLocation = adUseClient
conn.Open "DRIVER={MySQL ODBC 8.0 Unicode Driver}" _
& ";SERVER=" & credentials.getServer _
& ";DATABASE=" & credentials.getDB _
& ";UID=" & credentials.getUsername _
& ";PWD=" & credentials.getPassword
' Set spCommand = New ADODB.Command
spCommand.Prepared = True
spCommand.CommandText = "`testProc`"
spCommand.CommandType = adCmdStoredProc
spCommand.CommandTimeout = 30
spCommand.ActiveConnection = conn
Set param1 = spCommand.CreateParameter("inputCode", adVarChar, adParamInput, 2, Range("B1").Value)
spCommand.Parameters.Append param1
Set rs = spCommand.Execute()
Sheets.Add
ActiveSheet.Range("A2").CopyFromRecordset rs
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
End Sub
To be complete, here is my SQL SP (which runs just fine):
To define a testing table:
CREATE TABLE `testTable` (
`someCode` VARCHAR(2) NOT NULL COLLATE 'utf8mb4_general_ci',
`text` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`color` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci'
)
COMMENT='testTable meant for testing'
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;
To fill the testing table:
INSERT INTO db.`testTable`
(`someCode`, `text`, `color`)
VALUES
('S8', 'apple', 'red'),
('S8', 'banana', 'yellow'),
('PB', 'car', 'black'),
('S8', 'grapes', 'white'),
('TR', 'car', 'purple'),
('PB', 'car', 'orange');
and finally, to add a Stored Procedure:
DELIMITER //
CREATE DEFINER=`admin`#`%` PROCEDURE `testProc`(IN `inputCode` VARCHAR(2))
LANGUAGE SQL
DETERMINISTIC
READS SQL DATA
SQL SECURITY DEFINER
COMMENT 'test procedure'
BEGIN
DECLARE validCode varchar(1);
SET validCode = IF((COALESCE(inputCode, '') = '') OR (inputCode = '0'), 'N', 'Y');
SELECT * FROM db.`testTable`
WHERE (`someCode` LIKE if(validCode = 'Y', inputCode, '%'))
ORDER BY `color` ASC;
END //
DELIMITER ;
Online Demo
I'm grateful for any help, since I'm at a loss.
Please find an alternative way of retrieving the rows (which does not change the outcome unfortunately):
Option Explicit
Enum enums 'necessary because of late binding
adUseClient = 3 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/cursorlocationenum?view=sqlallproducts-allversions
adOpenStatic = 3 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/cursortypeenum?view=sqlallproducts-allversions
adCmdStoredProc = 4 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/commandtypeenum?view=sqlallproducts-allversions
adVarChar = 200 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/datatypeenum?view=sqlallproducts-allversions
adParamInput = 1 'https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/parameterdirectionenum?view=sqlallproducts-allversions
End Enum
Sub excelmysql()
Dim conn As Object
Dim spCommand As Object
Dim param1 As Object
Dim rs As Object
Set conn = CreateObject("ADODB.Connection")
Set spCommand = CreateObject("ADODB.Command")
Set param1 = CreateObject("ADODB.Parameter")
Set rs = CreateObject("ADODB.Recordset")
conn.CursorLocation = adUseClient
conn.Open "DRIVER={MySQL ODBC 8.0 Unicode Driver}" _
& ";SERVER=" & credentials.getServer _
& ";DATABASE=" & credentials.getDB _
& ";UID=" & credentials.getUsername _
& ";PWD=" & credentials.getPassword
' Set spCommand = New ADODB.Command
spCommand.Prepared = True
spCommand.CommandText = "`testProc`"
spCommand.CommandType = adCmdStoredProc
spCommand.CommandTimeout = 30
spCommand.ActiveConnection = conn
Set param1 = spCommand.CreateParameter("inputCode", adVarChar, adParamInput, 2, Range("B1").Value)
spCommand.Parameters.Append param1
rs.CursorLocation = adUseClient
rs.CursorType = adOpenStatic
On Error Resume Next
On Error GoTo 0
If (MsgBox("This will clear Sheet: ""test"" in the activeWorkbook if it exists. Do you wish to continue?", vbYesNoCancel + vbExclamation, "Delete Sheet test?") = vbYes) Then
Sheets("test").Delete
If Err <> 0 Then
Err.Clear
End If
Else
MsgBox "User cancelled execution. Please rename your sheet 'test' to something else if it exists and try again.", vbOKOnly + vbInformation, "Execution stopped"
conn.Close
Exit Sub
End If
Set rs = spCommand.Execute()
Sheets.Add after:=Sheets(Sheets.Count)
Sheets(Sheets.Count).Name = "test"
Dim row As Long
Dim i As Long
row = 1
rs.moveLast
rs.moveFirst
Do While Not rs.EOF
For i = 0 To rs.Fields.Count - 1
If (row = 1) Then
Sheets("test").Cells(row, i + 1) = rs.Fields.Item(i).Name
Else
Sheets("test").Cells(row, i + 1) = rs.Fields.Item(i).Value
End If
Next
If (row > 1) Then
rs.moveNext
End If
row = row + 1
Loop
' ActiveSheet.Range("A2").CopyFromRecordset rs
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
End Sub
Thank you for your time!
Cheers,
Niels
In VBS, taking the following example into consideration, what (if any) results are returned by the Open() method of the ADODB.Recordset object?
Dim CN : Set CN = CreateObject("ADODB.Connection")
Dim RS : Set RS = CreateObject("ADODB.Recordset")
CN.Open connectionString
RS.Open ("INSERT INTO db.table (username, computer_name, count) VALUES ('Whoever', 'Whatever', '99');", CN, 3)
In this example, calling RS.MoveFirst() results in an error, suggesting that no records were actually returned by the call to RS.Open(), despite the query running successfully. The error I receive is -
Either BOF or EOF is True, or the current record has been deleted. Requested operation requires a current record.
The reason I require this is because an ID is auto generated by the query, and I need that ID for my code. I can of course run a SELECT query, but it seems odd that results wouldn't be returned.
You need to pass something back for the ADODB.Recordset to be instantiated.
Dim CN : Set CN = CreateObject("ADODB.Connection")
Dim RS : Set RS = CreateObject("ADODB.Recordset")
CN.Open connectionString
RS.Open ("INSERT INTO db.table (username, computer_name, count) VALUES ('Whoever', 'Whatever', '99'); SELECT LAST_INSERT_ID();", CN, 3)
Personally I'd be inclined to write this using ADODB.Command instead of passing a SQL statement directly to ADODB.Recordset.
It will also guard against SQL Injection as it builds a parametrised query.
Something like;
Dim cmd: Set cmd = CreateObject("ADODB.Command")
Dim rs, id
Const adCmdText = 1
Const adParamInput = 1
Const adVarWChar = 202
Const adInteger = 3
Dim sql: sql = "INSERT INTO db.table (username, computer_name, count) VALUES (?, ?, ?); SELECT LAST_INSERT_ID();"
With cmd
.ActiveConnection = connectionString
.CommandType = adCmdText
.CommandText = sql
Call .Parameters.Append(.CreateParameter("#username", adVarWChar, 50))
Call .Parameters.Append(.CreateParameter("#computer_name", adVarWChar, 50))
Call .Parameters.Append(.CreateParameter("#count", adInteger, 4))
Set rs = .Execute(, Array("Whoever", "Whatever", 99))
If Not rs.EOF Then id = rs(0)
End With
Not sure what you are asking for in the comment but if you mean how do you use this approach with RS.Open this should help;
Dim cmd: Set cmd = CreateObject("ADODB.Command")
Const adCmdText = 1
Const adParamInput = 1
Const adVarWChar = 202
Const adInteger = 3
Const adOpenStatic = 3
Dim sql: sql = "INSERT INTO db.table (username, computer_name, count) VALUES (?, ?, ?); SELECT LAST_INSERT_ID();"
With cmd
.ActiveConnection = connectionString
.CommandType = adCmdText
.CommandText = sql
Call .Parameters.Append(.CreateParameter("#username", adVarWChar, 50))
Call .Parameters.Append(.CreateParameter("#computer_name", adVarWChar, 50))
Call .Parameters.Append(.CreateParameter("#count", adInteger, 4))
.Parameters("#username").Value = "Whoever"
.Parameters("#computer_name").Value = "Whatever"
.Parameters("#count").Value = 99
End With
Dim rs: Set rs = CreateObject("ADODB.Recordset")
Dim id
Call rs.Open(cmd, , adOpenStatic)
If Not rs.EOF Then id = rs(0)
Update
I've now read a bit about LAST_INSERT_ID() and this stood out to me in the documentation
From MySQL 5.7 Reference Manual - 13.14 Information Functions
The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENT value generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.
This means that as long as you maintain the same connection you can execute multiple commands and still get back an ID specific to your connection.
Dim conn: Set conn = CreateObject("ADODB.Connection")
Dim cmd: Set cmd = CreateObject("ADODB.Command")
Dim rs, id
Const adCmdText = 1
Const adParamInput = 1
Const adVarWChar = 202
Const adInteger = 3
Const adExecuteNoRecords = &H00000080
Const adOpenStatic = 3
Dim sql: sql = "INSERT INTO db.table (username, computer_name, count) VALUES (?, ?, ?);"
Call conn.Open(connectionString)
With cmd
Set .ActiveConnection = conn
.CommandType = adCmdText
.CommandText = sql
Call .Parameters.Append(.CreateParameter("#username", adVarWChar, 50))
Call .Parameters.Append(.CreateParameter("#computer_name", adVarWChar, 50))
Call .Parameters.Append(.CreateParameter("#count", adInteger, 4))
Set rs = .Execute(, Array("Whoever", "Whatever", 99), adExecuteNoRecords)
End With
Call rs.Open("SELECT LAST_INSERT_ID;", conn, adOpenStatic)
id = rs(0)
Set cmd = Nothing
Call conn.Close()
Set conn = Nothing
Try like this:
RS.Open ("INSERT INTO db.table (username, computer_name, count) VALUES ('Whoever', 'Whatever', '99');
SELECT LAST_INSERT_ID();", CN, 3)
ie, you can use the SELECT LAST_INSERT_ID() query to get the ID of the inserted row.
My query does not return any values even though the table has the records. I am trying to retrieve an employeed id based on the name entered. I keep getting the message "No employee id". I am a new learner as far as Access VBA is concerned. I have worked with Access tables and other tables without issues. I did validate that the form field has the correct value and is being captured in the variable strEmpName
Set cnn1 = New ADODB.Connection
mydb = "C:\accesssamp\Documents\Tasks.accdb"
strCnn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & mydb
cnn1.Open strCnn
'This statement added here just to indicate that I am getting the value
strEmpName = cboEmployeeName.Text ' Getting employee name from form field
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = CurrentProject.Connection
.CommandText = "SELECT [EmployeeId] FROM [Employees] " & _
"WHERE [EmployeeName] = [strEmpName]"
.CommandType = adCmdUnknown
.Parameters.Append cmd.CreateParameter( _
"[strEmpName]", adChar, adParamInput, 50)
.Parameters("[strEmpName]") = strEmpName
End With
' Execute the Query and return the employeeId
Set rstEmp = cmd.Execute
If rstEmp.RecordCount < 1 Then
MsgBox "No Employee Id"
Else
MsgBox rstEmp(0).Value
End If
Your sample had multiple issues. I'm unsure whether this is exactly what you want, but it works without error on my system.
With cmd
'.ActiveConnection = CurrentProject.Connection
.ActiveConnection = cnn1
.CommandText = "SELECT [EmployeeId] FROM [Employees] " & _
"WHERE [EmployeeName] = [strEmpName]"
.CommandType = adCmdUnknown ' or adCmdText; either works
.Parameters.Append cmd.CreateParameter( _
"strEmpName", adVarChar, adParamInput, 255, strEmpName)
End With
' Execute the Query and return the employeeId
Set rstEmp = cmd.Execute
'If rstEmp.RecordCount < 1 Then
If rstEmp.BOF And rstEmp.EOF Then
MsgBox "No Employee Id"
Else
MsgBox rstEmp(0).value
End If
Notes:
I assumed you want to run your query from the cnn1 connection to that other database instead of from CurrentProject.Connection.
Supply a compatible data type for CreateParameter. And for a text parameter, supply a value for its maximum length. Finally include the value for the parameter.
rstEmp.RecordCount was returning -1, which is less than 1, so your code displayed "No Employee Id" even when the recordset was not empty. Instead of checking RecordCount, check whether the recordset is empty.
How do you insert multiple values into a Lookup Field in an Access database using ASP?
(I've tried a few approaches, so I'm not even sure which code to show as an attempt.)
For a sample table named [Agents] with a multi-value Lookup Field named [Languages] ...
the following VBScript code represents one way to add a new Agent named "Maria" who speaks both English and Spanish
Option Explicit
Dim con, cmd, rst, newID
Const adInteger = 3
Const adVarWChar = 202
Const adParamInput = 1
Const adOpenStatic = 3
Const adLockOptimistic = 3
Set con = CreateObject("ADODB.Connection")
con.Open _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\Users\Public\Database1.accdb"
' insert all fields *except* multi-value Lookup Field
Set cmd = CreateObject("ADODB.Command")
cmd.ActiveConnection = con
cmd.CommandText = "INSERT INTO Agents (AgentName) VALUES (?)"
cmd.Parameters.Append cmd.CreateParameter("?", adVarWChar, adParamInput, 255, "Maria")
cmd.Execute
Set cmd = Nothing
' get AutoNumber ID of newly-inserted record
Set rst = CreateObject("ADODB.Recordset")
rst.Open "SELECT ##IDENTITY", con, adOpenStatic, adLockOptimistic
newID = rst(0).Value
rst.Close
Set rst = Nothing
' insert multi-value Lookup Field values
Set cmd = CreateObject("ADODB.Command")
cmd.ActiveConnection = con
cmd.CommandText = "INSERT INTO Agents (Languages.Value) VALUES (?) WHERE AgentID=?"
cmd.Parameters.Append cmd.CreateParameter("?", adVarWChar, adParamInput, 255)
cmd.Parameters.Append cmd.CreateParameter("?", adInteger, adParamInput)
cmd.Prepared = True
cmd.Parameters(1).Value = newID
' first value
cmd.Parameters(0).Value = "English"
cmd.Execute
' second value
cmd.Parameters(0).Value = "Spanish"
cmd.Execute
Set cmd = Nothing
con.Close
Set con = Nothing
While this may answer the immediate requirements of the question, it is important to note that:
Access SQL support for manipulating Lookup Fields is incomplete and can be inconsistent from one development environment to another,
"Microsoft strongly recommends against using Access in web applications" (ref: here), and
Seasoned Access developers recommend against using Lookup Fields (ref: here) except in very specific circumstances (e.g., for integration with SharePoint).
I was wondering if in ms-access through vb6 (ADODB) i can have the security benefits of parameterized queries
Set Prm = CmdEnn.CreateParameter("pText1", adBSTR, adParamInput)
Prm.Value = pText1
Cmd.Parameters.Append Prm
without using stored procedures. So having something like:
Cmd.CommandText = "select * from ..."
Cmd.CommandType = adCmdText
instead of
Cmd.CommandText = "stored_query_name"
Cmd.CommandType = adCmdStoredProc
#KekuSemau,
Cmd.CommandText = "select * from tablename where column like #pText1"
Cmd.CommandType = adCmdText
Set Prm = CmdEnn.CreateParameter("pText1", adBSTR, adParamInput)
Prm.Value = random_variable
Cmd.Parameters.Append Prm
it worked like this, but in the end of the day, i didn't use it for other reasons. i don't recall if i had to use single quotes around it or not.