tags:

views:

307

answers:

5

Alright, here's an odd one from an MS Access database I'm running.

I have a SQL query:

SELECT * 
FROM [Service Schedule]
WHERE ID=2 
  AND Volume <= 3000 
  AND Term='Monthly'
  AND special = 'Regular'
ORDER BY volume

When I put that into the SQL view of the query builder, I get 2 records, one with a volume of 0 and one with a volume of 3000.

When I use this code:

sSQL = "SELECT * FROM [Service Schedule] WHERE ID=2 AND Volume <= 3000 AND Term='Monthly' and special = 'Regular' ORDER BY volume"            
Set rsServiceSched = CurrentDb.OpenRecordset(sSQL, dbOpenDynaset, dbSeeChanges)

** To see what I'm getting from the query in the code, I'm using Debug.Print to output the recordcount and the volume.

I only get 1 record, the one with the volume of 0.

Here's where it gets really strange...

When I change Volume <= 3000 to Volume < 3000 I get one record (volume = 0)

When I change Volume <= 3000 to Volume = 3000 I get one record (volume = 3000)

Anyone spot anything blatantly wrong with what I'm doing?

+1  A: 

I'm not sure what you're doing for PK values of that table but when you say: "ID=2" that sounds fishy to me because I always use surrogate keys (never natural) and ID is always the PK. Since you aren't joining to any tables this tells me you should always expect one row/tuple from your result.

If ID isn't your PK would you mind letting me know via the comments?

Perhaps you need to iterate over the result set using: Set.MoveNext

Russ
ID isn't the PK of that table.
KevenDenen
Also, this is all in Visual Basic for Applications inside a Microsoft Access frontend that is attached to a SQL server.
KevenDenen
I think Eric has it spot on - you need to loop through your set and continue getting the next result out of the Set. See the following link for a loose example: http://www.access-programmers.co.uk/forums/showthread.php?t=146504
Russ
+1  A: 

It sounds like you are expecting to 'see' all of the records, but I think you are just retrieving the first record. I say this because you are seeing what would be the first record with each case. You will probably need to move to the next record in your recordset in order to see the next one.

rsServiceSched.MoveNext
David Walker
I added some information explaining how I am "seeing" all the records using the RecordCount method.
KevenDenen
In Access you can't rely on a recordset recordcount until you have moved to the last record.
dsteele
@dsteele: Correction you can't rely on a DAO recordset's recordcount until you have moved to the last record. You can always rely on an ADO recordset's recordcount property, even when fetching asynchronously, and navigating the EOF will not change it; depending on cursor type etc it may be -1 (i.e. not supported) but still it will not change.
onedaywhen
A: 

You are probably not looping through your result set. rsServiceSched is merely pointing to the first record, which will be the one with the lowest volume (bacuase of your order by clause). Now you need to do something with that, then advance to the next record

This example might help you...

Galghamon
+2  A: 

Digression: A discussion of the Recordcount of DAO recordsets

The recordcount of a DAO recordset is not guaranteed accurate until after a .MoveLast, but if any records are returned by the recordset, .RecordCount will be 1 or more.

Note that a table-type recordset will return an accurate .RecordCount immediately, without the .MoveLast, but keep in mind that you can't open a table-type recordset on a linked table. Also, be careful and don't assume you're getting the recordset type you want unless you've explicitly specified it. While dbOpenTable is the default recordset type, if the table or SQL string can't be opened as a table-type recordset, it will fall over to opening a dynaset. Thus, you can think you're opening a table-type recordset with this because table-type is the default and you've passed a table name:

  Set rs = CurrentDB.OpenRecordset("MyTable")

but you have to remember that just because you pass it a table, it won't necessarily open a table-type recordset, and the recordcount won't necessarily be accurate. If you really want to be sure you're opening a table-type recordset, you need to specify that explicitly:

  Set rs = CurrentDB.OpenRecordset("MyTable", dbOpenTable)

If "MyTable" is a linked table, that will throw an error. If you have a mix of linked tables and local tables, you'll have to use two different methods to obtain a table-type recordset. Otherwise (i.e., if you're not specifying the recordset type and letting it be table-type when possible and a dynaset when not), you need to know when you need to .MoveLast to get an accurate .RecordCount. If you really want to be efficient in that case, you'll test the .Type of the recordset:

  Set rs = CurrentDB.OpenRecordset("MyTable")
  If rs.Type = dbOpenDynaset Then
     rs.MoveLast
  End If

At that point, you'll have an accurate .RecordCount property whether the recordset opened as table-type or as a dynaset.

But keep in mind that it's very seldom that you need to use a full recordset to get a recordcount. Usually, you will examine the .RecordCount only to see if your recordset has returned any records, and in that case, you don't need an accurate count.

Likewise, if you're going to walk through the full recordset, you'll eventually have an accurate RecordCount. That is, it would be senseless to do this:

  Set rs = CurrentDB.OpenRecordset("MyTable")
  rs.MoveLast
  If rs.RecordCount > 0 Then
     .MoveFirst
     Do Until rs.EOF
       [something or other]
       .MoveNext
     Loop
  End If
  Debug.Print rs.RecordCount

The .MoveLast is completely unneeded in that context as you don't need to know the exact count at that point in the code.

Also, keep in mind that in some contexts where you really do need to know the exact .RecordCount, it may be more efficient to just use the built-in Access DCount() function, or to do something like this:

  Dim lngRecordCount As Long
  lngRecordCount = CurrentDB.OpenRecordset("SELECT COUNT(*) FROM MyTable")(0)

Or:

  lngRecordCount = DBEngine.OpenDatabase(Mid(CurrentDB.TableDefs("MyTable").Connect, 11)).TableDefs("MyTable").RecordCount

There are all sorts of efficient shortcuts to get the accurate RecordCount without forcing the recordset pointer to travel to the last record.

BTW, one of the reason the RecordCount for a pure Table-Type record is accurate is because it doesn't have to be calculated -- Jet/ACE maintains the RecordCount property as part of its regular operations.

Digression: A discussion of the Recordcount of ADO recordsets

By @onedaywhen

For an ADO recordset, the RecordCount property will always be the final value. That is, unlike DAO, if you check its value it cannot subsequently change. Navigating EOF does not affect the RecordCount value in any way for ADO.

This is true even when fetching records asynchronously (something DAO recordsets does not explicitly support): that is, even when the recordset is not yet full, the RecordCount property still reflects the final value (not the number of records fetched so far, as for DAO).

Some combinations of CursorLocation and CursorType will cause RecordCount to always be -1, meaning the property is not supported. But, again, this will remain constant i.e. if it is initially -1 then it will always be -1.

The applicable combinations for the Access database engine for which RecordCount is not supported are adOpenForwardOnly and adOpenDynamic but only when using a server side cursor location. (Note the Access database engine doesn't actually support dynamic cursors: instead adOpenDynamic is overloaded for the user to provide an optimization 'hint' to the engine that the recordset's Source is dynamic SQL code).

Note that setting the recordset's Filter property will change the RecordCount to reflect the number of records after the filter has been applied.

David-W-Fenton
I added a credit to @onedaywhen for the ADO section. I don't understand why you're not just creating a new answer for that. My post was about DAO Recordcount, you could post about ADO Recordcount. We could both get reputation points then. I'd really prefer that you strip the ADO discussion of this and put it in a separate answer. The two topics have so little to do with each other I don't think they belong together.
David-W-Fenton
A: 

This is DAO, you need to movelast to get the full recordcount; otherwise, you'll just get 1 if records exist and 0 if they do not.

Jeff O
First, they are actually using DAO; adOpenForwardOnly is an ADO cursor type. Second, adOpenForwardOnly does support the RecordCount property with the Access database engine when using a client side cursor (.CursorLocation = adUseClient).
onedaywhen