简体   繁体   中英

Odd Results from an SQL query in MS Access

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?

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 (ie, 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 ie 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.

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

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

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...

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM