Could do with some cursor help please (1 Viewer)

Lol999

Registered User.
Local time
Today, 04:11
Joined
May 28, 2017
Messages
184
As it says, I think the root of my problems are in the cursor settings but I'm struggling to nail it down.
I have some code below and basically it's comparing two tables and finding the total number of records that have no match.
It works so far but it keeps returning a total of -1 which I know is indicative of a cursor but I can't seem to bottom it out.

Could someone point me in the right direction please?

Code:
Set adoCon2 = CreateObject("ADODB.Connection")
adoCon2.Open "Provider = Microsoft.ACE.OLEDB.12.0; " & _
"Data Source = " & strDB & ";" & _
"Persist Security Info = False;"

strSQL4 = "SELECT Tbl_DataTest.EnterName " & _
"FROM Tbl_DataTest LEFT JOIN Tbl_Employee ON Tbl_DataTest.[EnterName] = Tbl_Employee.[EmpName] " & _
"WHERE (((Tbl_Employee.EmpName) Is Null));"


adoRs2.Open strSQL4, adoCon2

 totals = adoRs2.RecordCount

Debug.Print totals
adoRs2.Close
adoCon2.Close
Set adoRs2 = Nothing
Set adoCon2 = Nothing
 

jleach

Registered User.
Local time
Today, 07:11
Joined
Jan 4, 2012
Messages
308
I don't work with ADO too much, but I believe a -1 indicates that yes, there are records, but not necessarily the full count. I think in order to get the full count, you must first traverse all of the records in the table (this forces a fetch for all records, rather than the typical paging method). I think in this case the -1 means that records were found, but a full count is not yet available.

You can probably use the ADO recordset to navigate to the last record, then read the RecordCount again.

With that said, if you want to get a count of matches, why not query for it instead? Take your existing query and wrap it in a COUNT(*). This will return one record from the query and is typically much faster (it allows the SQL engine to perform the count rather than relying on ADO recordset mechanics to do so).

hth
 

isladogs

MVP / VIP
Local time
Today, 12:11
Joined
Jan 14, 2017
Messages
18,209
I also can't see any reason for involving ADO at all

This query SQL should give you the record count:
Code:
SELECT Count(Tbl_DataTest.EnterName) AS RecordCount
FROM Tbl_DataTest LEFT JOIN Tbl_Employee ON Tbl_DataTest.[EnterName] = Tbl_Employee.[EmpName]
WHERE (((Tbl_Employee.EmpName) Is Null));

For info, doing a record count on a linked table ALWAYS gives -1
Try this code on a database with both local & linked tables:

Code:
Sub TableFieldRecordCount()
    For Each tdf In CurrentDb.TableDefs
       Debug.Print tdf.Name, tdf.RecordCount, DCount("*", tdf.Name)
    Next tdf
End Sub

For local tables tdf.RecordCount & DCount give the same result
For linked tables, DCount works correctly but tdf.RecordCount = -1

Finally, I have absolutely no idea what this has to do with cursors!
 
Last edited:

jleach

Registered User.
Local time
Today, 07:11
Joined
Jan 4, 2012
Messages
308
>> doing a record count on a linked table ALWAYS gives -1 <<

For ADO? I know for DAO it gives the count of the accessed records (which is why we should traverse them all via .MoveLast prior to reading a recordcount).

>> no idea what this has to do with cursors! <<

I think in non-Access RDBMs scenario, where a cursor is a position within a set of records (typically avoided for performance reasons: SQL likes to be set-based). Recordsets in DAO/ADO don't use the same terminology but it's basically the current position.
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 19:11
Joined
May 7, 2009
Messages
19,233
this is an excerpt from an article about ADO and DAO, it is indeed a CursorType issue.


#3: Avoid moving violations
Moving through the records in a Recordset is a common task, but doing so comes with a few hazards. Using any move method will
generate an error if the Recordset object is empty. The most reliable check uses a simple If statement before executing a move method:

If Not(rst.BOF And rst.EOF) Then
...

The BOF property returns True when the current position is before the first record. Similarly, EOF returns True when the current position is
just beyond the last record. If both properties are True simultaneously, the Recordset is empty.

Tip: Including the Not operator is a matter of efficiency. If you expect the Recordset to contain records most of the time, it's more efficient
to include Not in the check.

#4: Expose an erroneous record count
The previous technique uses BOF and EOF to expose an empty Recordset before code can generate an error. Another way to avoid this
type of error is to use the RecordCount property to count the records as follows:
If rst.RecordCount <> 0 Then
This statement is a bit unpredictable with ADO because some ADO Recordset objects return -1 as the count. Use a Static or Keyset cursor to return a true record count in an ADO Recordset.

When using DAO, you must remember to populate (fully) the Recordset before counting, as follows:

rst.MoveFirst
If rst.RecordCount <> 0 Then
...

DAO populates with records as they are needed, and consequently, its RecordCount property considers only the records already
accessed. ADO, on the other hand, defines the RecordCount property as the total number of records, period. That's why this second
check for an empty Recordset is less flexible and can be troublesome.

Whether using RecordCount to check for the existence of records or to count records, you must remember the differences between DAO
and ADO:

When using DAO, populate (fully) the Recordset before checking for an empty Recordset or a total count.
When using ADO, use a static or keyset cursor to return the actual record count.

Tip: Don't use MoveLast with a large Recordset to check for an empty Recordset. Instead, use MoveNext. If RecordCount returns 1, you know the Recordset isn't empty.

#5: You can't move beyond the first or last record

In tip #3, I told you to use the BOF and EOF properties to test for an empty Recordset. You'll also use them individually when moving through records. Moving beyond the first or last record returns an error. For instance, the following code returns an error when the MoveNext method moves beyond the last record:

If rst![fieldname] = value Then
...do something...
rst.MoveNext

If you're moving forward, use EOF to inhibit the error that occurs when you move beyond the last record, as follows:

Do While Not rst.EOF
If rst![fieldname] = value Then
...do something...
End If
rst.MoveNext
Loop

As long as EOF equals False, meaning the current position is a valid record, this loop executes the If statement. Once the EOF property is True, the check eliminates the error that would otherwise occur by bypassing the MoveNext method in the loop.
 

Lol999

Registered User.
Local time
Today, 04:11
Joined
May 28, 2017
Messages
184
Apologies folks, I was unusually tired when I posted the appeal for help.
Just to clarify, this piece of code is run from an Excel spreadsheet and is intended to check data about to be written into a database against a, as it were, master list in the same database.
The two tables are not linked, the table Tbl_DataTest is there solely for the purpose of verifying data integrity before attempting to write to the main table.

I asked for help on cursors because a good few other posts I have read on the 'net say it is a cursor type problem.
 

isladogs

MVP / VIP
Local time
Today, 12:11
Joined
Jan 14, 2017
Messages
18,209
jleach
in non-Access RDBMs scenario, where a cursor is a position within a set of records
Ah... that wasn't how I was reading it

The 2 articles below explain different ways of doing record counts and compares the speed of each method. Generally the speed difference only noticeable for large recordsets

https://www.devhut.net/2016/02/24/ms-access-vba-get-record-count/

http://rogersaccessblog.blogspot.co.uk/2009/04/what-is-fastest-way-to-return-record.html

For ADO? I know for DAO it gives the count of the accessed records (which is why we should traverse them all via .MoveLast prior to reading a recordcount

Correct - but if you want to loop through a set of tables as in the code I gave, then use DCount instead

arnelgp
That was a good article. Can you give the link so I can read the whole thing

lol999

Suggest you try the aggregate query code I supplied before:
Code:
SELECT Count(Tbl_DataTest.EnterName) AS RecordCount
FROM Tbl_DataTest LEFT JOIN Tbl_Employee ON Tbl_DataTest.[EnterName] = Tbl_Employee.[EmpName]
WHERE (((Tbl_Employee.EmpName) Is Null));
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 19:11
Joined
May 7, 2009
Messages
19,233
i fogot the link but the
title is "10+ mistakes to avoid when using VBA Recordset objects by Susan Harkins"
 

CJ_London

Super Moderator
Staff member
Local time
Today, 12:11
Joined
Feb 19, 2013
Messages
16,607
interesting article and I learnt something - wasn't aware of the 'is' issue with ADO.

But is this statement right?

ADO is the default object library for Access, but DAO is still available
I know Access has moved to ACE, but in my experience, DAO is the default - you need to load the Microsoft ActiveX Data Objects 2.1 Library for ADO. I know historically MS changed from DAO to ADO for 2000 but changed it back again in 2003.
 

Lol999

Registered User.
Local time
Today, 04:11
Joined
May 28, 2017
Messages
184
jleach


lol999

Suggest you try the aggregate query code I supplied before:
Code:
SELECT Count(Tbl_DataTest.EnterName) AS RecordCount
FROM Tbl_DataTest LEFT JOIN Tbl_Employee ON Tbl_DataTest.[EnterName] = Tbl_Employee.[EmpName]
WHERE (((Tbl_Employee.EmpName) Is Null));

Hi Ridders and thanks for the suggestion. One thing I forgot to mention (sorry I'm unusually tired at the moment due to a health issue) was that I need to pass the value to a variable as I want to use it in a MsgBox.
Does an expression in sql work that way?

Regards, Lol
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 19:11
Joined
May 7, 2009
Messages
19,233
You need to set the cursor type of the recordset. Have you not read what i post.
You do this before opening adoRs2:

With AdoRs2
.CursorType=adOpenStatic
.cursorlocation=adUseClient
.LockType=adLockOptimistic
.Open strSQL4, adoCon2

Totals=.RecordCount
End With
 
Last edited:

Lol999

Registered User.
Local time
Today, 04:11
Joined
May 28, 2017
Messages
184
Arnel no I haven't, I just did a quick reply to Ridders whilst I cooked my tea.
To be honest what you have offered looks exactly what I was expecting it to be.
Many thanks for your help.
 

Lol999

Registered User.
Local time
Today, 04:11
Joined
May 28, 2017
Messages
184
Well I tried the code supplied kindly by Arnelgp and I keep getting error 3001 "arguments are of the wrong type, out of acceptable range or in conflict with one another"
The problem seems to lie with the cursor location and locktype since they are the lines highlighted when debugged.
Commenting out the location and locktype commands returns a value of -1.
I cannot see a problem and have read all I can on cursor locations and locktypes, perhaps someone can see where we are going wrong?

Code:
Code:
Set adoCon2 = CreateObject("ADODB.Connection")
adoCon2.Open "Provider = Microsoft.ACE.OLEDB.12.0; " & _
"Data Source = " & strDB & ";" & _
"Persist Security Info = False;"

strSQL4 = "SELECT Tbl_DataTest.EnterName " & _
"FROM Tbl_DataTest LEFT JOIN Tbl_Employee ON Tbl_DataTest.[EnterName] = Tbl_Employee.[EmpName] " & _
"WHERE (((Tbl_Employee.EmpName) Is Null))ORDER BY Tbl_DataTest.EnterName DESC;"



With adoRs2
.CursorType = adOpenStatic
.CursorLocation = adUseClient
.LockType = adLockOptimistic
.Open strSQL4, adoCon2
totals = .RecordCount
End With
 
Debug.Print totals
adoRs2.Close
adoCon2.Close
Set adoRs2 = Nothing
Set adoCon2 = Nothing
 

isladogs

MVP / VIP
Local time
Today, 12:11
Joined
Jan 14, 2017
Messages
18,209
Not a direct answer to your question but a reminder that there is no need to involve ADO at all. See earlier posts such as post 3
 

Lol999

Registered User.
Local time
Today, 04:11
Joined
May 28, 2017
Messages
184
Not a direct answer to your question but a reminder that there is no need to involve ADO at all. See earlier posts such as post 3
Evening Ridders :)
As I said in one of my posts this is being run from an Excel sheet and I need a value to place in a message box to display to the numpties inputting data where they are going wrong.
I don't know of a method of taking the value from the query and assigning it to a variable but then again I'm pretty thick at times :)
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 19:11
Joined
May 7, 2009
Messages
19,233
i tried to recreate your table
either local and linked mysql table.
the code runs without error.
Code:
Private Sub test()
Dim adoCon2 As ADODB.Connection
Dim adoRs2 As ADODB.Recordset
Dim total As Integer
'Dim strDB As String
'strDB = Environ("UserProfile") & "\Desktop\Database\Test4.accdb"
Set adoCon2 = CreateObject("ADODB.Connection")
Set adoRs2 = CreateObject("ADODB.Recordset")

adoCon2.Open "Provider=MIcrosoft.ACE.OLEDB.12.0; " & _
"Data Source=" & strDB & ";" & _
"Persist Security Info=False;Mode=Share Deny None;"

Dim strSQL4 As String
'table linked to mysql - passed
'strSQL4 = "select customers1.id from customers1 left join customers on customers1.id=customers.id where customers.id is null"

'table in access - passed
'strSQL4 = "select count(*) from tbl_datatest left join tbl_employee on tbl_datatest.entername=tbl_employee.empname where tbl_employee.empname is null"

strSQL4 = "select tbl_datatest.entername from tbl_datatest left join tbl_employee on tbl_datatest.entername=tbl_employee.empname where tbl_employee.empname is null"

With adoRs2
    .CursorType = adOpenStatic
    .CursorLocation = adUseClient
    .LockType = adLockPessimistic
    .Open strSQL4, adoCon2

    If Not (.BOF And .EOF) Then
        'used on ridders count()
        'Debug.Print .Fields(0)
        'total = .Fields(0)
        
        'used on my code
        total = .RecordCount
    End If
    .Close
End With
Set adoRs2 = Nothing
Set adoCon2 = Nothing
Debug.Print total
End Sub
 

Lol999

Registered User.
Local time
Today, 04:11
Joined
May 28, 2017
Messages
184
With adoRs2
.CursorType = adOpenStatic
.CursorLocation = adUseClient
.LockType = adLockPessimistic
.Open strSQL4, adoCon2

If Not (.BOF And .EOF) Then
'used on ridders count()
'Debug.Print .Fields(0)
'total = .Fields(0)

'used on my code
total = .RecordCount
End If
.Close
End With
Set adoRs2 = Nothing
Set adoCon2 = Nothing
Debug.Print total
End Sub
[/code][/QUOTE]

Hi arnelgp and thanks for the continued support. Unfortunately I keep getting the same error despite copying your code and pasting directly into excel vba.
I open another recordset prior to this one but close it and set its connection and itself to nothing when done.
For the sake of completeness I have included the full code activated by the button press incase of any conflicts the previous code are causing.

Many thanks, Lol

Code:
Sub Button5_Click()
Dim intStartRow As Integer
Dim intEndRow As Integer
Dim i As Integer
Dim adoCon
Dim adoCon2
Dim adoRs
Dim adoRs2
Dim strSQL4 As String
Dim Total As Integer
Dim strSQL As String
Dim strDBName As String
Dim strMyPath As String
Dim strDB As String

strDBName = "Labour Hours Analysis_be.accdb"
strMyPath = "C:\Users\Lol\Desktop\"
strDB = strMyPath & "" & strDBName

Set adoCon = CreateObject("ADODB.Connection")
adoCon.Open "Provider = Microsoft.ACE.OLEDB.12.0; " & _
"Data Source = " & strDB & ";" & _
"Persist Security Info = False;"



strSQL = "Select * From Tbl_DataTest;"

strSQL2 = "INSERT INTO Tbl_DataTest (TrueName) SELECT EmpName FROM Tbl_Employee;"
 
Set adoRs = CreateObject("ADODB.Recordset")

Set adoRs2 = CreateObject("ADODB.Recordset")

adoRs.CursorType = 2

adoRs.LockType = 3

'Open the tblDataTest table using the SQL query held in the strSQL varaiable
adoRs.Open strSQL, adoCon

intStartRow = 5
intEndRow = 100

For i = intStartRow To intEndRow

adoRs.AddNew
adoRs(1).Value = Cells(i, 3) 'Column 3=C
adoRs.Update
Next

adoRs.Close
adoCon.Close
Set adoRs = Nothing
Set adoCon = Nothing

Set adoCon2 = CreateObject("ADODB.Connection")
adoCon2.Open "Provider = Microsoft.ACE.OLEDB.12.0; " & _
"Data Source = " & strDB & ";" & _
"Persist Security Info = False;"

strSQL4 = "SELECT Tbl_DataTest.EnterName " & _
"FROM Tbl_DataTest LEFT JOIN Tbl_Employee ON Tbl_DataTest.[EnterName] = Tbl_Employee.[EmpName] " & _
"WHERE (((Tbl_Employee.EmpName) Is Null))ORDER BY Tbl_DataTest.EnterName DESC;"



With adoRs2
    .CursorType = adOpenStatic
    .CursorLocation = adUseClient
    .LockType = adLockPessimistic
    .Open strSQL4, adoCon2

    If Not (.BOF And .EOF) Then
        'used on ridders count()
        'Debug.Print .Fields(0)
        'total = .Fields(0)
        
        'used on my code
        Total = .RecordCount
    End If
    .Close
End With
 
Debug.Print Total
adoRs2.Close
adoCon2.Close
Set adoRs2 = Nothing
Set adoCon2 = Nothing


Exit Sub


End Sub
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 19:11
Joined
May 7, 2009
Messages
19,233
Goto button5 click event and press f9. Go to excel an press the button. This will bring you in debug mode. To continue oress f8. Just keep on pressing f8 until yhe error appears. Take note which line got the error and post ut.
 

Lol999

Registered User.
Local time
Today, 04:11
Joined
May 28, 2017
Messages
184
Hi arnelgp this is the offending line:

.CursorLocation = adUseClient
 

Users who are viewing this thread

Top Bottom