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 :)
Related
Hello I keep getting an error message saying the path is wrong. I'm new at VBA Code and I just don't know how to solve it. This is what I wrote. I keep checking the path every time and copy-pasting the path and yet it can't find it. Sorry for my English - not main language.
Error message is:
Run time errror
Could not find file A:\Proccess_Assig1.accdb'.
The code is
Private Sub btn_buscar_Click()
Dim rs As New ADODB.Recordset
Dim conn As New ADODB.Connection
Dim strsql As String
strsql = "Select * from TBlClientes Where id= " & txtCliente.Value & ""
conn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=A:\Proccess_Asig1.accdb;persist security Info=False"
conn.Open
rs.Open strsql, conn
If rs.EOF Then
MsgBox "Id no existe"
txtCliente.Value = Nothing
txtNombre.Value = Nothing
txtApellido.Value = Nothing
txtTelefono.Value = Nothing
Else
txtCliente.Value = rs.Fields("Nombre")
txtApellido.Value = rs.Fields("ApellidoPaterno")
txtTelefono.Value = rs.Fields("Teléfono")
End If
rs.Close
conn.Close
Set conn = Nothing
Set rs = Nothing
End Sub
Check that you have the actual valid path A:\Proccess_Assig1.accdb doesn't seem like a standard path, try c:\Proccess_Assig1.accdb
I am trying to retrieve data to excel form a database in MS access. However the recordcount property for recordset always return -1 though for other purposes the code is working fine.
The code I am using is as follows :
`Sub datarecordset()
Dim cn As adodb.Connection
Dim oRs As adodb.Recordset
Set cn = CreateObject("ADODB.Connection")
DBPath = "C:\[databse path]" & "\[database name].accdb"
dbWs = "[excel sheet name]"
scn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DBPath
dsh = "[" & "[excel sheet name]" & "$]"
cn.Open scn
Dim sSQL As String
Dim F As Integer
sSQL = "Select 'W',a.[Subledger],NULL,sum(a.[Amount]) from GL_Table a where a.[Opex_Group] = 10003 and year(a.[G/L Date]) = " & Year(Sheets("Repairs").Cells(1, 4)) & " and month(a.[G/L Date]) = " & Month(Sheets("Repairs").Cells(1, 4))
sSQL = sSQL & " group by " & "a.[Subledger],(year(a.[G/L Date])),(month(a.[G/L Date]))"
Set oRs = cn.Execute(sSQL)
Debug.Print oRs.RecordCount
oRs.Close
....... further code to print to excel here
cn.Close
End Sub`
The code will fetch data in recordset and write in excel. But since the recordset property is not returning the recordcount so can't print values of various fields in recordset to different cells of excel worksheet.
I searched on google and understood that I need to declare the recordset type and for that I have to use connection.open in place of connection.execute. But I am trying to change the code then it gives the error object variable or With variable not defined.
Any quick help will be welcome. Thanks.
The link by #BitAccesser provides a valid solution. Quick how-to-implement in your situation:
Instead of Set oRs = cn.Execute(sSQL)
Set oRS = CreateObject("ADODB.Recordset")
oRS.CursorLocation = adUseClient
oRS.Open sSQL, cn
ADO's recordcount property returns -1 when ADO cannot determine the number of records or if the provider or cursor type does not support RecordCount. That last one is true for this case.
To avoid this, there are several solutions. The most simple one is to use a client-side cursor, which I just demonstrated, but more alternative solutions are provided in the links by #BitAccesser
You may also specify the CursorType as the third argument to open the RecordSet as follows, which is optional
The first two lines, leaving blank or selecting adOpenDynamic, do not give the record count.
The remaining ones work OK.
1-RS.Open SqlStr, Conn
2-RS.Open SqlStr, Conn, adOpenDynamic
(Erik's solution)
- 3-RS.CursorLocation = adUseClient
Other Options also work fine, please note 4- and 6- which do not require a seperate line
- 4-RS.Open SqlStr, Conn, adOpenKeyset
- 5-RS.Open SqlStr, Conn, adOpenKeyset AND RS.CursorLocation = adUseClient
- 6-RS.Open SqlStr, Conn, adOpenStatic AND RS.CursorLocation = adUseClient
- 7-RS.Open SqlStr, Conn, adOpenStatic
BR, Çağlar
You can still use the Execute method but you need to set the correct cursor type. The recordset is created automatically with cursor type adOpenForwardOnly. This results in oRs.RecordCount = -1. adOpenKeySet is the correct cursor type to correctly show oRs.RecordCount.
Note: The LockType is irrelevant in this case.
Set oRs = cn.Execute(sSQL)
oRs.Close
oRs.CursorType = adOpenKeyset
oRs.Open
Debug.Print oRs.RecordCount
Closing the recordset, changing the cursor type and reopening the recordset worked fine for me (Access 2016 on Windows 7).
I am trying to set up a form to use a disconnected ADODB.Recordset as its source.
The issue I have is that changes are not saved into the original Access table upon closing the form and replying "Yes" to the prompt. What am I missing ?
Note: Please don't tell me the method is useless, it's just a POC with a local table, I plan to try later with a more "distant" recordset.
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Private Sub Form_Load()
Set conn = New ADODB.Connection
conn.Open CurrentProject.Connection
Set rs = New ADODB.Recordset
With rs
rs.CursorLocation = adUseClient
rs.Open "select * from amsPor", conn, adOpenStatic, adLockBatchOptimistic
Set rs.ActiveConnection = Nothing
End With
Set Me.Recordset = rs
conn.Close
End Sub
Private Sub Form_Unload(Cancel As Integer)
Select Case MsgBox("Save changes ?", vbQuestion + vbYesNoCancel)
Case vbNo
'do nothing
Case vbYes
conn.Open CurrentProject.Connection
rs.ActiveConnection = conn
rs.UpdateBatch
rs.Close
conn.Close
Set conn = Nothing
Case vbCancel
Cancel = True
End Select
End Sub
Steps to Reproduce:
Take a small table which has a primary key
Generate an automatic form with it
Save the form.
Add the above code to the form, replacing the table name in the select clause.
Empty the Record Source property of the form.
Save and Close the form.
You can open the form and make changes to the data. Upon close, you will be prompted for saving your changes.
EDIT: I wonder if the issue might be in the CurrentProject.Connection ?
In the debug window, I typed ? CurrentProject.Connection and got the following:
Provider=Microsoft.ACE.OLEDB.12.0;User ID=Admin;Data Source=\\xxxxxx\yyyy$\Documents\AMS.accdb;Mode=Share Deny None;Extended Properties="";Jet OLEDB:System database=C:\Users\G828992\AppData\Roaming\Microsoft\Access\System.mdw;Jet OLEDB:Registry Path=Software\Microsoft\Office\14.0\Access\Access Connectivity Engine;Jet OLEDB:Database Password="";Jet OLEDB:Engine Type=6;Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Global Bulk Transactions=1;Jet OLEDB:New Database Password="";Jet OLEDB:Create System Database=False;Jet OLEDB:Encrypt Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without Replica Repair=False;Jet OLEDB:SFP=False;Jet OLEDB:Support Complex Data=True;Jet OLEDB:Bypass UserInfo Validation=False
I came here looking for the same answer as you and after tons of googling and trial and error I finally was able to perform exactly what you are attempting to do. I understand this is an old post but I did not see any answers that actually provided an answer that would allow what you are attempting to do work. I will use your example and try and apply what I had to change and add to get it to work properly.
Dim rs As ADODB.Recordset
Dim conn As ADODB.Connection
Private Sub Form_Load()
If CurrentProject.Connection.State = adStateOpen Then CurrentProject.Connection.Close
Set conn = New ADODB.Connection
conn.Open CurrentProject.Connection.ConnectionString
Set rs = New ADODB.Recordset
rs.CursorLocation = adUseClient
rs.Open "select * from amsPor", conn, adOpenForwardOnly, adLockBatchOptimistic
If Not rs Is Nothing Then
If Not rs.ActiveConnection Is Nothing Then Set rs.ActiveConnection = Nothing
If Not (rs.eof And rs.BOF) Then
Set Me.Recordset = rs
End If
If conn.State = adStateOpen Then
conn.Close
End If
End If
Call AddNewRecord(Me.Recordset)
End Sub
Private Sub AddNewRecord(ByRef rs As ADODB.Recordset)
On Error Resume Next
If Not rs Is Nothing Then
If rs.Supports(adAddNew) Then
rs.AddNew
rs.Fields("FirstName").Value = "John"
rs.Fields("LastName").Value = "Doe"
If rs.Supports(adUpdate) Then rs.Update
End If
End If
If Err.Number <> 0 Then
Debug.Print "AddNewRecord Err Msg: " & Err.Description
Err.Clear
End If
End Sub
Private Sub Form_Unload(Cancel As Integer)
Select Case MsgBox("Save changes ?", vbQuestion + vbYesNoCancel)
Case vbYes
Call UpdateDbWithRS(Me.Recordset)
Case vbCancel
Cancel = True
Case Else
' Nothing.
End Select
End Sub
Private Sub UpdateDbWithRS(ByRef rs As ADODB.Recordset)
If Not rs Is Nothing Then
If CurrentProject.Connection.State = adStateOpen Then CurrentProject.Connection.Close
Set conn = New ADODB.Connection
conn.Open CurrentProject.Connection.ConnectionString
rs.ActiveConnection = conn
If rs.Supports(adUpdateBatch) Then
rs.UpdateBatch
If Not conn Is Nothing Then
If conn.State = adStateOpen Then conn.Close
Set conn = Nothing
End If
If Not rs Is Nothing Then
If rs.State = adStateOpen Then rs.Close
Set rs = Nothing
End If
End If
End If
End Sub
With the code above I was able to Add a Record to my Recordset and verify it did not show up in my database table. Then when I performed the UpdateDbWithRS the Record that I had added to the Recordset, previously, was now pushed to my database table.
The biggest changes I had to do with your code was changing conn.Open CurrentProject.Connection to conn.Open CurrentProject.Connection.ConnectionString, adding in the code If CurrentProject.Connection.State = adStateOpen Then CurrentProject.Connection.Close to fix the error I was receiving about the connection already being opened. Then the final biggest change I made was replacing your CursorType of adOpenStatic to adOpenForwardOnly. I am not sure if that last change is truly required but I used it based on a disconnected RecordSet example I found on this Microsoft Support Site.
When you use a disconnected recordset, you do not get the benefit of automatically updating changes to the tables. You need to actually run SQL Update and Insert Statements to save your data.
First of all, your code look perfect and should works as well, but...
Solution 1
As per my experience i'd suggest to forget about such of functionality. Several years ago i struggled with the same problem. I did not found any solution, but i'm almost sure that the access database used in multiuser environment could not be updated, because Jet/ACE engine does not allow to update static recordset when other user had made changes in a meanwhile (changes are rejected).
I resolved this issue by using "temporary table" binded with form:
DELETE * FROM ~TableName;
INSERT INTO ~TableName SELECT * FROM TableName;
User can edit records till Form is opened. On Form_Unload event i run query like this:
UPDATE t1 SET Field1 = t2.Field1,
Field1 = t2.Field2 ... and so on
FROM TableName As t1 INNER JOIN ~TableName AS t2 ON t1.Key = t2.Key
Note, that insertion and deletion of records (if its allowed) should be handled separately.
Solution2
Use dynamic cursor and does not disconnect recordset from database ;)
Catch changes by using Form.Dirty property.
None of your code has anything to do with DISCONNECTED RECORDSETS. Your recordsets are connected. A disconnected recordset can be saved to file as xml or binary. There is no underlying database.
Note we make the disconnected recordset.
Sub Randomise
Randomize
Set rs = CreateObject("ADODB.Recordset")
With rs
.Fields.Append "RandomNumber", 4
.Fields.Append "Txt", 201, 5000
.Open
Do Until Inp.AtEndOfStream
.AddNew
.Fields("RandomNumber").value = Rnd() * 10000
.Fields("Txt").value = Inp.readline
.UpDate
Loop
.Sort = "RandomNumber"
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
End Sub
Here are the states
ConnectionState
The ConnectionState enumeration is used to identify the state of a connector space object. The CSEntry.ConnectionState property contains one of the values of this enumeration.
Connected
The connector space object is connected to a metaverse object.
ExplicitlyConnected
The connector space object is connected explicitly by a member of the MIISAdmins or MIISOperators group to a metaverse object by the account joiner.
Disconnected
The connector space object is not connected to an metaverse object but may be a candidate for connection to a metaverse object in the future.
DisconnectedByFilter
The connector space object was disconnected by the connector filter rules.
Explicitly Disconnected
The connector space object is not connected to a metaverse object and will not be a candidate for connection to a metaverse object in the future.
Placeholder The connector space object exists implicitly in the connected directory, but has not been imported.
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
I have a form in Access that I am trying to populate during it's Current event using the fields from an ADO Recordset. I am using Sql Server for the database and am using the recordset to try to populate to corresponding fields on the form with what is in the recordset. Right now it works like this:
Private Sub Form_Open(Cancel As Integer)
On Error GoTo Err_FormOpen
Set rst As new ADODB.Recordset
With rst
.ActiveConnection = CurrentProject.AccessConnection
.CursorType = adOpenKeyset
.LockType = adLockOptimistic
.Source = "SELECT * FROM [Outage Summary] ORDER BY OutageSummaryID"
.Open
End With
Set Me.Recordset = rst
End Sub
Private Sub Form_Current()
OutageSummaryID.Value = Me.Recordset!OutageSummaryID
Dispatcher.Value = Me.Recordset!Dispatcher
StartDateTime.Value = Me.Recordset!StartDateTime
Location.Value = Me.Recordset!Location
CityRelated.Value = Me.Recordset!CityRelated
Scheduled.Value = Me.Recordset!Scheduled
CustomerEquip.Value = Me.Recordset!CustomerEquip
DispatchBusHrs.Value = Me.Recordset!DispatchBusHrs
TotalCount.Value = Me.Recordset!TotalCount
Substation.Value = Me.Recordset!Substation
CompletionDateTime.Value = Me.Recordset!CompletionDateTime
CustCallDateTime.Value = Me.Recordset!CustCallDateTime
FirstRespDateTime.Value = Me.Recordset!FirstRespDateTime
Feeder.Value = Me.Recordset!Feeder
SwitchingSchedule.Value = Me.Recordset!SwitchingSchedule
Cause.Value = Me.Recordset!Cause
ActionTaken.Value = Me.Recordset!ActionTaken
Me.ME.Value = Me.Recordset!ME
MI.Value = Me.Recordset!MI
End Sub
But I would like the current subroutine to work something like this:
Dim fld As ADODB.Field
Dim nam As String
For Each fld In Me.Recordset.Fields
Debug.Print fld
nam = fld.Name
Me.Controls(nam).Value = fld.Value
Next
With the code as it stands I am getting an error "The recordset is not updatable"
Thanks for any help!
This is because you are not binding to the recordset. It would be better if you stored your query and attached it to the form as the recordsource so that way it will bind it to the form. Then you set the record source of each field in design mode and no code is needed and it will be updateable depending on your permission to the SQL Server.