Excel VBA stored procedure with parameters not working? - vba

I am struggling bit here with a stored procedure with parameters in VBA. The code below without parameters working fine but with parameters not working.
My code:
Sub CopyDataFromDatabase()
Dim Conn As ADODB.Connection
Dim Rs As ADODB.Recordset
Dim Fields As ADODB.Field
Dim Cmd As ADODB.Command
Set Conn = New ADODB.Connection
Set Cmd = New ADODB.Command
Set Rs = New ADODB.Recordset
Conn.Open "My connection string here--------"
Cmd.CommandType = adCmdStoredProc
Cmd.Parameters.Append Cmd.CreateParameter("#Division", adVarChar, adParamInput, 40)
Cmd.Parameters("#Division").Value = "South"
Cmd.Parameters.Append Cmd.CreateParameter("#Area", adVarChar, adParamInput, 40)
Cmd.Parameters("#Area").Value = "IT"
Cmd.CommandText = "My SP here------"
Set Rs = Cmd.Execute
On Error GoTo CloseRecordset
Worksheets.Add
For Each Fields In Rs.Fields
ActiveCell.Value = Fields.Name
ActiveCell.Font.Bold = True
ActiveCell.Font.Underline = True
ActiveCell.HorizontalAlignment = xlCenter
ActiveCell.Interior.Color = RGB(0, 128, 255)
ActiveCell.Font.Color = RGB(255, 255, 255)
ActiveCell.Offset(0, 1).Select
Next Fields
Range("A1").Select
Range("A2").CopyFromRecordset Rs
CloseRecordset:
Rs.Close
Set Rs = Nothing
Set Cmd = Nothing
CloseConnection:
Conn.Close
Set Conn = Nothing
End Sub
When I run, its not giving any error, just showing like executing but no result
Can anybody suggest where I am doing wrong? Thanks

I have successfully declared a variant array and populated the parameters (in order!) into that array, then passed to the array into the execute method to execute a stored procedure.
Assuming your stored proc expects 'Division' then 'Area', something like this may do the trick:
Sub CopyDataFromDatabase()
Dim Conn As ADODB.Connection
Dim Rs As ADODB.Recordset
Dim Fields As ADODB.Field
Dim Cmd As ADODB.Command
'New variable
Dim v_Params(1 To 2) As Variant 'assuming you have 2 parameters
Set Conn = New ADODB.Connection
Set Cmd = New ADODB.Command
Set Rs = New ADODB.Recordset
Conn.Open "My connection string here--------"
v_Params(1) = "South"
v_Params(2) = "IT"
With Cmd
.ActiveConnection = Conn
.CommandType = adCmdStoredProc
.CommandText = "My SP here------"
.CommandTimeout = 0
Set rs = .Execute(, v_Params)
End With
See if that works, as I am currently using this method successfully. I didn't see the need to modify the rest of your subroutine.
I used the With Cmd and End With to avoid fully qualifying the reference each time.
Updated
The issue, found by the author of the question, was that the SP was timing out when parameters were passed into it. The resolution was to set the CommandTimeout property to 0.

Related

Is it possible to pass a parameter from MS Access to a SQL Server stored procedure and display the output in a datasheet form?

Users in MS Access enter values in a field that is passed as a parameter to a stored procedure in a SQL Server database. The stored procedure will return a result set with two columns. I would like to display that result set in a datasheet form. The code below almost does that, but will only display the final row of the result set. I confirmed that the stored procedure is being passed to the db correctly and that the test values I'm using should return 4 rows. The code in comments are a few of the many things that I have tried that didn't work.
Private Sub btnBulkLink_Click()
Dim cmd As New ADODB.Command
Dim rst As New ADODB.Recordset
With cmd
.CommandType = adCmdStoredProc
.ActiveConnection = "Driver=SQL Server Native Client 11.0;Server=xxxxxxxxxxxxxxxxx;Database=xxxxx;Trusted_Connection=Yes"
.CommandText = "dbo.usp_FindResolutionsLinkedToServices"
.CommandTimeout = 180
.NamedParameters = True
End With
cmd.Parameters.Append cmd.CreateParameter("#PprsRefINP", adLongVarChar, adParamInput, Len(txtBulkLink.Value), txtBulkLink.Value)
Set rst = cmd.Execute
DoCmd.OpenForm FormName:="ServiceResolutionLink", View:=acFormDS, DataMode:=acFormReadOnly, WindowMode:=acHidden
' Set Forms!ServiceResolutionLink.Recordset = rst
With rst
Do While Not .EOF
Forms("ServiceResolutionLink").txtPprsRef = rst!PprsRef
Forms("ServiceResolutionLink").txtSubmissionId = rst!SubmissionId
.MoveNext
Loop
End With
' Forms("ServiceResolutionLink").txtPprsRef = rst!PprsRef
' Forms("ServiceResolutionLink").txtSubmissionId = rst!SubmissionId
' Forms("ServiceResolutionLink").Visible = True
DoCmd.OpenForm FormName:="ServiceResolutionLink", View:=acFormDS
rst.Close
End Sub
Ok, we assume you setup a pass-though query. (and it set with returns records = true).
And if your datasheet is a REAL sub form (setup as datasheet - not a table).
Then this code will work:
Private Sub Command2_Click()
With CurrentDb.QueryDefs("qryPassR")
.SQL = "exec dbo.GetHotels2 #City = '" & "Banff" & "'"
End With
Me.MyDataSheet.Form.RecordSource = "qryPassR"
End Sub
And if you are using a real datasheet (form that does not exist), then you can have the form set to
Query.PassR
The trick (or challenge) then becomes to have the form NOT load until such time you setup the query with the correct stored procedure call.
I was in the midst of testing suggestions from others when my boss figured this out. Here is our final code:
Private Sub Form_Load()
Dim rst As New ADODB.Recordset
Dim cmd As New ADODB.Command
Dim strPPRSRef As String
Dim cn As New ADODB.Connection
With cn
.Provider = "sqloledb"
cn.Properties("Data Source").Value = "[server]"
cn.Properties("Initial Catalog").Value = "[db]"
cn.CursorLocation = adUseClient
' Windows authentication.
cn.Properties("Integrated Security").Value = "SSPI"
' .CursorLocation = adUseClient
End With
cn.Open
With cmd
.CommandType = adCmdStoredProc
.ActiveConnection = cn
.CommandText = "dbo.usp_FindResolutionsLinkedToServices"
.CommandTimeout = 180
.NamedParameters = True
End With
strPPRSRef = Forms("Resolution_Tracker").txtBulkLink.Value
cmd.Parameters.Append cmd.CreateParameter("#PprsRefINP", adLongVarChar, adParamInput, Len(strPPRSRef), strPPRSRef)
With rst
.CursorType = adOpenStatic
.CursorLocation = adUseClient
End With
Set rst = cmd.Execute
Set Me.Recordset = rst
rst.Close
Set rst = Nothing
cn.Close
Set cn = Nothing
End Sub

VBA ADODB parameter as dynamic Excel Range

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

ASP SQL Server Connection

<%
DIM objConn
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.ConnectionString = "Data Source=123.123.12.123,1234;Database=DatabaseName;User Id=Usernm;Password=abcd1234;"
objConn.Open
DIM mySQL
mySQL = "SELECT * FROM [Users] WHERE [User ID]='1'"
DIM objRS
Set objRS = Server.CreateObject("ADODB.Recordset")
objRS.open(mySQL, objConn)
Response.Write objRS("FullName")
objRS.Close
Set objRS = Nothing
objConn.Close
Set objConn = Nothing
%>
I want to connect to a SQL Server Database, read the data and close the connection. I have studied the examples and came up with this. But its not working. Please guide me. Where am I going wrong?
Some answers have suggested wrapping logic into functions there is no need for this.
It's just a lot of fluff that isn't needed, just use the ADODB.Command object. There are hundreds of ways to approach this but a method I have found to work time and again is let the ADODB.Command object do the work then return your results into an Array using .GetRows() method of the ADODB.Recordset object.
That way you can close off both the ADODB.Recordset and ADODB.Command objects quickly and work just with the Array.
Dim conn, cmd, rs, sql, data, search
'Assume value to query comes from a Request Collection.
search = Request("myvalue") & ""
conn = "Data Source=123.123.12.123,1234;Database=DatabaseName;User Id=Usernm;Password=abcd1234;"
sql = "select from mytable where this = ?"
Set cmd = Server.CreateObject("ADODB.Command")
With cmd
'No need to handle connection let ADODB.Command create and destory it.
.ActiveConnection = conn
.CommandType = adCmdText
.CommandText = sql
.Parameters.Append(.CreateParameter("#myparam", adVarWChar, adParamInput, 50))
.Parameters("#myparam").Value = search
Set rs = .Execute()
If Not rs.EOF Then data = rs.GetRows()
Call rs.Close()
Set rs = Nothing
End with
Set cmd = Nothing
'ADODB.Connection is closed when ADODB.Command is destroyed.
If IsArray(data) Then
rows = UBound(data, 2)
For row = 0 To rows
'Return first column of the current row
Call Response.Write("First Column of Row " & row & " is '" & data(0, row) & "'<br />"
Next
Else
Call Response.Write("No records")
End If
Dim rs, dbConn
Function OpenDB()
Set dbConn = Server.CreateObject("ADODB.Connection")
dbConn.ConnectionTimeout = 300
dbConn.CommandTimeout = 300
dbConn.Open "Data Source=123.123.12.123,1234;Database=DatabaseName;User Id=Usernm;Password=abcd1234;"
End Function
Function CloseDB()
Set rs = Nothing
if ucase(TypeName(dbConn)) = "CONNECTION" then
dbConn.Close
Set dbConn = Nothing
end if
End Function
Function OpenRecordSet(recset, tablename)
Call OpenDB()
Set recset = Server.CreateObject("ADODB.Recordset")
recset.Open tablename, dbConn, 0, 1
End Function
Function CloseRecordSet(recset)
Set recset = Nothing
Call CloseDB()
End Function
Then use
<%
Call OpenDB()
sql = "select from mytable where this = 'that'"
Set rs = dbConn.Execute(sql)
if not rs.EOF then
' do your stuff!
end if
Call CloseDB()
%>
http://www.shiningstar.net/articles/articles/database/datafunctions.asp?ID=AW

Excel VBA executing SQL Server stored procedure - result set throwing error 3704

I am trying to execute a SQL Server stored procedure from Excel VBA. The procedure returns rows into a result set object. However, while running the code, it throws an error:
3704 Operation is not allowed when the object is closed
Note:
There is no problem with the database connection because Select query running on the same connection object are working fine.
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim cmd As ADODB.Command
Dim prm As ADODB.Parameter
Dim rst As New ADODB.Recordset
Set cn = New ADODB.Connection
Set cmd = New ADODB.Command
ThisWorkbook.initialize
cn.Provider = "sqloledb"
cn.Properties("Data Source").Value = ThisWorkbook.server
cn.Properties("Initial Catalog").Value = ThisWorkbook.db
cn.Properties("User ID").Value = "xxxxx"
cn.Properties("Password").Value = "xxxxx"
cn.Open
Set cmd = New ADODB.Command
cmd.CommandText = "Generate_KPI_Process_Quality_Check_RunTime"
cmd.CommandType = adCmdStoredProc
cmd.ActiveConnection = cn
Set prm = cmd.CreateParameter("#currentMonth", adChar, adParamInput, 255, cmb_month.Value)
cmd.Parameters.Append prm
Set prm = cmd.CreateParameter("#center", adChar, adParamInput, 255, cmb_center.Value)
cmd.Parameters.Append prm
rst.CursorType = adOpenStatic
rst.CursorLocation = adUseClient
rst.CursorLocation = adUseServer
rst.LockType = adLockOptimistic
rst.Open cmd
If (rst.BOF And rst.EOF) Then
'Some Code
End If
Put
SET NOCOUNT ON
in the stored procedure -- this will prevent output text generation like "1 record(s) updated".
You have to provide more parameters for the Open method of Recordset Object
try rst.Open cmd, cn
Use the Set keyword to assign the object:
Set cmd.ActiveConnection = cn
otherwise, the default property of the Connection object (which happen to be the connection string) will be assigned in lieu of the Connection object itself.
Just put another recordset that will contain resultsets
Dim rst1 As New ADODB.Recordset
SET rst1=rst.NextRecordset 'this will return the first resultset
If rst1.BOF or rst1.EOF Then...
'some code
End If

Running stored procedure from Excel

I am trying to run a stored procedure from Excel. I know how to do it without using dynamic dates but I need the date range to be dynamic.
Sub TestStoredProcedure()
Dim CServer As String
Dim CDatabase As String
Dim CLogon As String
Dim CPass As String
Dim StartDate As Date
Dim EndDate As Date
Dim TStartDate As String
Dim TEndDate As String
CServer = "111111" ' Your server name here
CDatabase = "111111" ' Your database name here
CLogon = "11111111" ' your logon here
CPass = "111111" ' your password here
Dim Cmd1 As New ADODB.Command
Dim rs As New ADODB.Recordset
Dim intTemp As Integer
Set Cmd1 = New ADODB.Command
Cmd1.ActiveConnection = cn
Cmd1.CommandText = "callstatisticsbyQ"
Cmd1.CommandType = adCmdStoredProc
Cmd1.Parameters.Refresh
Cmd1.Parameters(0).Value = Worksheets("Sheet2").Range("A1")
Cmd1.Parameters(1).Value = Worksheets("Sheet2").Range("A2")
Cmd1.Parameters(2).Value = Worksheets("Sheet2").Range("A3")
Set rs = Cmd1.Execute()
rs.Open Cmd1
Worksheets("Procedure Export").Range("A1").CopyFromRecordset rs
Call DumpSP("prcGetData", "", "", Worksheets("Procedure Export").Range("A1"))
End Sub
I get an error saying something about user defined type not defined.
To use ADO you click Tools->references in the VBA IDE & tick "Microsoft ActiveX Data Objects" - preferably the highest version thereof.
Additionally you use cn as the connection but its not defined in that sub (assuming its not global) & you will may need to Set Cmd1.ActiveConnection = cn.
Also take a look at this, it defines the input (adParaminput) paramaters in advance rather than using .Refresh which is pretty inefficient (takes a trip to the server)
Update for example:
rem for create procedure callstatisticsbyQ (#i int, #c varchar(10)) as select 1234;
Dim cn As ADODB.Connection
Dim Cmd1 As ADODB.Command
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
Set Cmd1 = New ADODB.Command
Set Cmd1 = New ADODB.Command
cn.Open "Provider=SQLNCLI10;Server=1.2.3.4;Database=x;Uid=x; Pwd=x;"
Set Cmd1.ActiveConnection = cn
Cmd1.CommandText = "callstatisticsbyQ"
Cmd1.CommandType = adCmdStoredProc
Cmd1.Parameters.Append Cmd1.CreateParameter("p1", adInteger, adParamInput, , Worksheets("Sheet2").Range("A1"))
Cmd1.Parameters.Append Cmd1.CreateParameter("p2", adVarChar, adParamInput, 20, Worksheets("Sheet2").Range("A2"))
Set rs = Cmd1.Execute()
MsgBox rs(0)
rs.Close
cn.Close