Call a PostgreSQL function with parameters via VBA - vba

Why do I get an error that my object is closed in the line Range("A1").CopyFromRecordset rcs?
Sub NamenAusBenutzerCmd_SO()
Dim cmd As New ADODB.Command
Dim rcs As New ADODB.Recordset
With cmd
.ActiveConnection = cnn
.CommandText = "myschema.myfunction"
.CommandType = adCmdStoredProc
.Parameters("myparameter").Value = "xyz"
Set rcs = .Execute()
Range("A1").CopyFromRecordset rcs
End With
End Sub
The connection is fine, the recordset executes but then I get the error 3704, the process is not permitted for a closed object.
By the way: The function itself runs directly in pgAdmin, so it´s ok.
And when I try Debug.Print rcs.Fields("myfunction").Value instead of the bold line above I get the error 3265, that an object with this name cannot be finded.
I watched YouTube videos, asked google and found exact the same way. I don´t see my mistake :-/
Sorry, I edit my question from here, because there is another thing I would like to ask you. I hope to increase the performance with the command. Instead of open and close a recordset 100 times in a loop like here:
With rcs
For i = 1 To 100
myparameter = Cells(i, 1).Value
.Open ("SELECT myschema.myfunction('" & myparameter & "')"), cnn, adOpenForwardOnly, adLockReadOnly
Cells(i, 2).Value = .Fields("myfunction").Value
.Close
Next i
End With
i hope to do it faster even with the command. I still don´t know why the way with .CommandType = adCmdStoredProc doesn´t run, but it runs this way:
With cmd
.ActiveConnection = cnn
For i = 1 To 100
myparameter = Cells(i, 1).Value
.CommandText = "SELECT myschema.myfunction('" & myparameter & "')"
.CommandType = adCmdText
Set rcs= .Execute
Cells(i, 2).Value = rcs.Fields("myfunction").Value
Next i
End With
But the performance is as bad as with the open-close-rcs.
So maybe I try to solve a thing which will not help me?

Related

VBA Recordset doesn't return all fields

I just startied working with this database and I have a small problem.
So the main idea behind this is to use VBA to get needed information from database that I can use later on.
I am using ADO recordset and connect sting to connect to server. All is fine apart from one problem: when I am creating RecordSet by using SQL request it only returns one field when i know there should me more. At the moment I think that RecordSet is just grabbing first result and storing it in but looses anything else that should be there. Can you please help me.
Here is my code:
'Declare variables'
Dim objMyConn As ADODB.Connection
Dim objMyCmd As ADODB.Command
Dim objMyRecordset As ADODB.Recordset
Dim fldEach As ADODB.Field
Dim OrderNumber As Long
OrderNumber = 172783
Set objMyConn = New ADODB.Connection
Set objMyCmd = New ADODB.Command
Set objMyRecordset = New ADODB.Recordset
'Open Connection'
objMyConn.ConnectionString = "Provider=SQLOLEDB;Data Source=Local;" & _
"Initial Catalog=SQL_LIVE;"
objMyConn.Open
'Set and Excecute SQL Command'
Set objMyCmd.ActiveConnection = objMyConn
objMyCmd.CommandText = "SELECT fldImage FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
objMyCmd.CommandType = adCmdText
'Open Recordset'
Set objMyRecordset.Source = objMyCmd
objMyRecordset.Open
objMyRecordset.MoveFirst
For Each fldEach In objMyRecordset.Fields
Debug.Print fldEach.Value
Next
At the moment Debug returns only one result when it should return two because there are two rows with the same OrderID.
The recordset only opens a single record at a time. You are iterating through all the fields in a single record. Not each record in the recordset.
If your query returns two records, you need to tell the Recordset to advance to the next one.
A query returns one recordset which has some number of records which have some number of fields.
You are iterating through the fields only for one record in the returned recordset.
You can do this with a few ways, but I generally do something like:
objMyRecordset.MoveFirst
Do
If Not objMyRecordset.EOF Then
debug.print "Record Opened - only returning 1 field due to SQL query"
For Each fldEach In objMyRecordset.Fields
Debug.Print fldEach.Value
Next
'this moves to the NEXT record in the recordset
objMyRecordset.MoveNext
Else
Exit Do
End If
Loop
Note that if you want to include more fields you will need to modify this line:
objMyCmd.CommandText = "SELECT fldImage FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
To include whatever additional fields you want returned.
In addition to the #enderland's answer, you can also have a disconnected RecordSet, that have all the values and fields ready for consumption. It's handy when you need to pass the data around or need to close the connection fast.
Here's a function that returns a disconnected RecordSet:
Function RunSQLReturnRS(sqlstmt, params())
On Error Resume next
' Create the ADO objects
Dim rs , cmd
Set rs = server.createobject("ADODB.Recordset")
Set cmd = server.createobject("ADODB.Command")
' Init the ADO objects & the stored proc parameters
cmd.ActiveConnection = GetConnectionString()
cmd.CommandText = sqlstmt
cmd.CommandType = adCmdText
cmd.CommandTimeout = 900 ' 15 minutos
collectParams cmd, params
' Execute the query for readonly
rs.CursorLocation = adUseClient
rs.Open cmd, , adOpenForwardOnly, adLockReadOnly
If err.number > 0 then
BuildErrorMessage()
exit function
end if
' Disconnect the recordset
Set cmd.ActiveConnection = Nothing
Set cmd = Nothing
Set rs.ActiveConnection = Nothing
' Return the resultant recordset
Set RunSQLReturnRS = rs
End Function
You are mixing up terms in your question which makes it unclear
In your first paragraph you describe a problem with "Fields", in the last paragraph you turn it into "Rows". Not exactly the same.
But whatever you are trying to achieve, the code you wrote will only return one field and one row.
If you want all FIELDS, your query should be:
objMyCmd.CommandText = "SELECT * FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
If you want all ROWS, your loop should be:
objMyRecordset.MoveFirst
If Not objMyRecordset.BOF Then
While Not objMyRecordset.EOF
debug.print objMyRecordset!fldImage
RS.MoveNext
Wend
End If

Want to run sql file through VBA

I am trying to connect to the Sybase server through VBA and run the sql file that contains a set of sql lines (sometimes more than 60 lines).
I have written the code as below. However, it is giving me error that " There is an incorrect statement around '\'"
Please help.
Sub sqltest()
Dim conn As ADODB.Connection
Dim cmd As ADODB.Command
Set conn = New ADODB.Connection
conn.Open "DRIVER={Sybase ASE ODBC Driver};UID=" & ThisWorkbook.Sheets("Sheet1").Cells(2, 13).Value & ";pwd=" & ThisWorkbook.Sheets("Sheet1").Cells(2, 14).Value & ";NA=<server address> ; CommandTimeout = 50000 ;ConnectionTimeout = 50000; ConnectionIdleTimeout = 50000;Connection LifeTime = 50000;LoginTimeout = 50000;AlternateServers = <server address>;"
conn.Open
Set cmd = New ADODB.Command
cmd.ActiveConnection = conn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "\\<IP address>\pag\OffShore PAG\Parallel Run\Reports\Sunil\Mymacros\sample macros\SQL NC DB.sql"
cmd.Execute
conn.Close
Set conn = Nothing
Set cmd = Nothing
End Sub
Why do it by VBA? I would do it manually using the "From other sources" in the "Data" ribbon menu item.
"From other sources"->"From Microsoft Query"->"".
Next specify your database, driver etc. and the SQL.

Using Stored Procedure in Classical ASP .. execute and get results

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

VBA in Access 2010 - Run-time Error 430

I’m getting a Run-time error '430': Class does not support Automation or does not support expected interface" on this line of code Set Me.lstResults.Recordset = rs or this Set Me![frmM_SearchForDocumentsSubForm].Form.Recordset = rs. I am trying to get the ADO Recordset based on a SQL stored procedure to appear in an unbound Listbox or Subform of an Access form. I’m on Win 7 Machine using Access 2010 connecting to SQL Server 2008:
On_Click event:
Private Sub cmdRun_Click()
'On Error Resume Next
Dim strSQL As String
'Stored procedure + parameters called from form
strSQL = "Exec sqlsp_searchalltables " & Me.txtTables & _
", " & "'%" & Me.txtSearchTerm & "%'"
OpenMyRecordset rs, strSQL
'debug - view procedure
Me.lblQuery.Caption = strSQL
Me.Repaint
Set Me.lstResults.Recordset = rs
'or this
'Set Me![frmM_SearchForDocumentsSubForm].Form.Recordset = rs
End Sub
I found some solutions for this error on the web and tried all of them to no avail. Most suggested checking the references which I did and verified.
I am able to successfully connect to the SQL server and have the results display in both a Listbox and Subform when I use DAO Querydef and a passthrough query or if I use this .listbox method:
With Me.lstResults
Do
strItem = rs.Fields("CLIENT_ID").Value
.AddItem strItem
rs.MoveNext
Loop Until rs.EOF
End With
I would prefer not to use the DAO method because I found I need the coding flexibility of ADO especially with connecting to multiple Recordsets in SQL. Thoughts?
FYI: My OpenMyRecordset public function in Module:
Option Compare Database
Option Explicit
Global con As New ADODB.Connection
Global rs As ADODB.Recordset
Global NoRecords As Boolean
Public Enum rrCursorType
rrOpenDynamic = adOpenDynamic
rrOpenForwardOnly = adOpenForwardOnly
rrOpenKeyset = adOpenKeyset
rrOpenStatic = adOpenStatic
End Enum
Public Enum rrLockType
rrLockOptimistic = adLockOptimistic
rrLockReadOnly = adLockReadOnly
End Enum
Public Function OpenMyRecordset(rs As ADODB.Recordset, strSQL As String, Optional rrCursor As rrCursorType, _
Optional rrLock As rrLockType, Optional bolClientSide As Boolean) As ADODB.Recordset
If con.STATE = adStateClosed Then
con.ConnectionString = "ODBC;Driver={SQL Server};Server=mysqlsvr;DSN=RecordsMgmt_SQLDB;UID=XXX;Trusted_Connection=Yes;DATABASE=RecordsManagementDB;"
con.Open
End If
Set rs = New ADODB.Recordset
With rs
.ActiveConnection = con
.CursorLocation = adUseClient
.CursorType = IIf((rrCursor = 0), adOpenDynamic, rrCursor)
.LockType = IIf((rrLock = 0), adLockOptimistic, rrLock)
.Open strSQL
If .EOF And .BOF Then
NoRecords = True
Exit Function
End If
End With
End Function
You definitely do not have to do the looping method to just to populate the listbox. I'm not familiar with the OpenMyRecordset command you used, but I suspect that something in its functionality is what is causing this error (i.e., it's not opening the recordset in a manner compatible with the listbox). This is how I connected to a local instance of SQL Server Express and was able to populate a listbox.
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
With cn
.ConnectionString = _
"Provider=SQLOLEDB;Data Source=localhost\SQLEXPRESS;" & _
"Initial Catalog=Northwind;Trusted_Connection=yes"
.Open
End With
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Source = "SELECT FirstName, LastName FROM Employees"
.LockType = adLockOptimistic
.CursorType = adOpenKeyset
.Open
End With
Set Me.lstTest.Recordset = rs
Set rs = Nothing
Set cn = Nothing
You will have to make sure that you have the Microsoft ActiveX Data Objects Library reference enabled in your project.

Invalid Use of Property in VB6 Error. How to fix this?

Private Sub aTbBar_Change()
Set con = New ADODB.Connection
With con
.CursorLocation = adUseClient
.ConnectionString = "Provider=Microsoft.jet.oledb.4.0;persist security info=false;data source=" & App.Path & "\Event_Participants.accde"
.Open
End With
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = con
.CursorType = adOpenDynamic
.Source = "select * from Participants"
.Open
'check from table if user and pwd matches
If rs.RecordCount <> 0 Then
rs.MoveFirst
While Not rs.EOF
If rs!Bar_Code_No = Val(Me.aTbBar) Then
Me.aTbName = rs!Full_Name
Me.aTbSection = rs!Section
Me.aTbArrtime = Time()
End If
rs.MoveNext
Wend
End If
.Close
Set rs = Nothing
End With
'save to the database
'check from table if user and pwd matches
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = con
.CursorType = adOpenDynamic
.LockType = adLockOptimistic
.Source = "select * from Participants"
.Open
If rs.RecordCount <> 0 Then
rs.MoveFirst
While Not rs.EOF
If rs!Bar_Code_No = Val(Me.aTbBar) Then
.Update
rs!Arr_Time = Me.aTbArrtime
End If
rs.MoveNext
Wend
End If
End With
rs.Close
Set rs = Nothing
End Sub
Invalid Use of Proper error always occur when I type in to that textbox name aTbBar
The error occurs at Me.aTbName = rs!Full_Name. Can you help me on this one. Sorry, im new in this forums and in VB. I really need help
The default property triggered for a TextBox is the Text property. So, if there is a TextBox with the name Text1, then this statement: Text1 = "Hello" would be equivalent to Text1.Text = "Hello". But I always prefer using the property name along with the control name, when accessing it(ie, Text1.Text = "Hello").
Anyway, test it out by using this line: Me.aTbArrtime.text = rs!Full_Name
Another thing that I have in mind is, if you have used some other component, say a custom made TextBox control (instead of the default one), and in the case of load failure, VB would replace the control(the custom made textbox) with a PictureBox in your forms. For checking that, click on the TextBox in the form and view it's properties. And see whether the control type is a TextBox. If it is a PictureBox, then double check whether your OCX or DLL for the custom made textbox is present in the project.
A small suggestion on your SQL code is that, you could have included the comparison in your query itself, instead of looping through all the records. For example:
.Source = "select * from Participants WHERE Bar_Code_No = " & Val(Me.aTbBar.Text) & " LIMIT 1"
This would return a single record if it matches the Bar_Code_No. After executing this query, you only need to check if it returns any record. If so, a match is found. Otherwise, no match is found. By this way, you can avoid looping, which might make your program Non-Responding if the number of records in the table Participants is enormously large !
Hope this would help you. Wish you good luck :)