Why RecordsAffected returns 0 for this query (1 Viewer)

KitaYama

Well-known member
Local time
Tomorrow, 06:26
Joined
Jan 6, 2022
Messages
1,575
I'm executing this sql.
But AffectedRecords returns 0.
The record is successfully added to the table.

Why is that?

Thanks.

Code:
sql = "INSERT INTO tblManuProcess ("
sql = sql & "ManuProcessManuFK, ManuProcessProcessFK, ManuProcessUserFK, "
sql = sql & "ManuProcessDate, ManuProcessCount, ManuProcessInspector"
sql = sql & ") VALUES ("
sql = sql & "P1,P2,P3,P4,P5,P6"
sql = sql & ")"

With CurrentDb.CreateQueryDef("", sql)
    .Parameters("P1") = txtManuPK
    .Parameters("P2") = ProcessFK
    .Parameters("P3") = cmbUser
    .Parameters("P4") = GetCurrentDate
    .Parameters("P5") = txtManuCount
    .Parameters("P6") = cmbInspector
    .Execute dbFailOnError
    .Close
    If CurrentDb.RecordsAffected > 0 Then
       fltr = fltr & "," & .OpenRecordset("select @@identity")(0)
     Else
        MsgBox "Failed"
        Exit Sub
     End If
End With
 
Last edited:

cheekybuddha

AWF VIP
Local time
Today, 22:26
Joined
Jul 21, 2014
Messages
2,321
DDL for the table:
Code:
CREATE TABLE [tblChapters] (
  [ChapterID] COUNTER,
  [BookFK] LONG,
  [ChapterNo] LONG,
  [ChapterTitle] VARCHAR(255),
  [TestDate] DATETIME
);
CREATE INDEX [ChapterID] ON [tblChapters]( [ChapterID] );
CREATE UNIQUE INDEX [PrimaryKey] ON [tblChapters]( [ChapterID] ) WITH PRIMARY;
-- Foreign Key Index will be created after with Constraints
-- CREATE INDEX [tblBookstblChapters] ON [tblChapters]( [BookFK] );

-- NOTE: tblBooks has not been defined
-- ALTER TABLE [tblChapters] ADD CONSTRAINT [tblBookstblChapters]
--   FOREIGN KEY ( [BookFK] )
--   REFERENCES [tblBooks] ( [BookID] )
--   ON UPDATE CASCADE
--   ON DELETE CASCADE;
 

KitaYama

Well-known member
Local time
Tomorrow, 06:26
Joined
Jan 6, 2022
Messages
1,575
As I said several posts earlier Only the following combination works.
Otherwise I receive an error or the result is always 0.

Code:
With CurrentDB.CreateQueryDef("",sql)
    ......
    .RecordsAffected
End With

CurrentDB.OpenRecordset("select @@identity")(0)

No matter I use variables or not. No matter I use .close or not.
Any other combination causes an error.
It seems .RecordsAffected is a member of CreateQueryDef and not CurrentDatabase. (I don't know the correct terminology)

My first tries and offered suggestion was using .OpenRecordset under with.
 
Last edited:

theDBguy

I’m here to help
Staff member
Local time
Today, 14:26
Joined
Oct 29, 2018
Messages
21,536
As I said several posts earlier Only the following combination works.
Otherwise I receive an error or the result is always 0.

Code:
With CurrentDB.CreateQueryDef("",sql)
    ......
    .RecordsAffected
End With

CurrentDB.OpenRecordset("select @@identity")(0)

No matter I use variables or not. No matter I use .close or not.
Any other combination causes an error.
It seems .RecordsAffected is a member of CreateQueryDef and not CurrentDatabase. (I don't know the correct terminology)

My first tries and offered suggestion was using .OpenRecordset under with.
I didn't post any code earlier, because David @cheekybuddha beat me to it, but this is what I had in mind, which is essentially the same as his idea.
Code:
Dim lngRecs As Long
Dim lngID As Long

With CurrentDb
    With .CreateQueryDef("",sql)
        .Parameters("P1") = ...
        ....
        .Execute dbFailOnError
        lngRecs = .RecordsAffected
    End With
    lngID = .OpenRecordset("SELECT @@IDENTITY")(0)
End With
If lngRecs > 0 Then
    fltr = fltr & "," & lngRecs
Else
    MsgBox "Failed"
End If
(untested, of course)
 

KitaYama

Well-known member
Local time
Tomorrow, 06:26
Joined
Jan 6, 2022
Messages
1,575
I didn't post any code earlier, because David @cheekybuddha beat me to it, but this is what I had in mind, which is essentially the same as his idea.
Code:
Dim lngRecs As Long
Dim lngID As Long

With CurrentDb
    With .CreateQueryDef("",sql)
        .Parameters("P1") = ...
        ....
        .Execute dbFailOnError
        lngRecs = .RecordsAffected
    End With
    lngID = .OpenRecordset("SELECT @@IDENTITY")(0)
End With
If lngRecs > 0 Then
    fltr = fltr & "," & lngRecs
Else
    MsgBox "Failed"
End If
(untested, of course)
@theDBguy Of course it will work.
Because .RecordsAffected is going back to .CreateQueryDef & .OpenRecordset to CurrentDB.

Thank you.
 

theDBguy

I’m here to help
Staff member
Local time
Today, 14:26
Joined
Oct 29, 2018
Messages
21,536
@theDBguy Of course it will work.
Because .RecordsAffected is going back to .CreateQueryDef & .OpenRecordset to CurrentDB.

Thank you.
Just to clarify something you said ealier, this will also work (but not the same as your situation).
Code:
strSQL = "UPDATE TableName SET Field1="Value1" WHERE Field2="SomeCriteria"
With CurrentDb
    .Execute strSQL, dbFailOnError
    MsgBox .RecordsAffected & " records were updated.", vbInformation, "Info"
End With
In other words, RecordsAffected is also a member of the CurrentDb object.
 

KitaYama

Well-known member
Local time
Tomorrow, 06:26
Joined
Jan 6, 2022
Messages
1,575
Just to clarify something you said ealier, this will also work (but not the same as your situation).
Code:
strSQL = "UPDATE TableName SET Field1="Value1" WHERE Field2="SomeCriteria"
With CurrentDb
    .Execute strSQL, dbFailOnError
    MsgBox .RecordsAffected & " records were updated.", vbInformation, "Info"
End With
In other words, RecordsAffected is also a member of the CurrentDb object.
Yes I know. I always use it. And it was why I used it in my code in post #1.
It was making me go crazy seeing it doesn't return 1 in my first post.

That was the main reason I asked for help.

Thanks again
 

theDBguy

I’m here to help
Staff member
Local time
Today, 14:26
Joined
Oct 29, 2018
Messages
21,536
Yes I know. I always use it. And it was why I used it in my code in post #1.
It was making me go crazy seeing it doesn't return 1 in my first post.

That was the main reason I asked for help.

Thanks again
I see. Okay. I just wanted to make sure to clarify what you said earlier, in case others see it in the future. This was what you said:
It seems .RecordsAffected is a member of CreateQueryDef and not CurrentDatabase. (I don't know the correct terminology)
Just to repeat, RecordsAffected is a member of both QueryDef and CurrentDb objects.
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 16:26
Joined
Feb 28, 2001
Messages
27,317
Referring to the MSVBA Language Specification v1.02 (dated 4/30/2014), the WITH statement creates a block. Given the way that blocks work in VBA, it implies that the object named by the WITH keyword is open for the duration of the WITH/END WITH range (i.e. the block). If there is a nested WITH/END WITH block, then the outer object is also available to the inner objects - though if there is an overlap in property names between the two, disambiguation would be required.

My idea here is a surmise, a hypothesis, but not a provable fact BECAUSE Microsoft does not publish that code for us to examine and thus answer. However, there is this concept called "active references" that is how Windows (and therefore Access) know that something is still busy. This "active reference" concept is why two people can both open a shared back-end through their private FE. One can do a SELECT and the other can, milliseconds later, do a DELETE of the same record, and yet the recordset for the SELECT doesn't go away instantly. Its "active references" count isn't zero so it has to wait until the recordset is closed or the auto-requery timer expires. It is also why you can read a shared file that you opened before someone else tried to delete it. It is also why you can't delete an open file like the .LDB or .LACCDB file.

Using the WITH CurrentDB.CreateQueryDefs clause, and because there was an implied reference action in the WITH clause, the "active references" count isn't zero inside the WITH block. Therefore the object properties referenced under the WITH clause remain viable. However, issuing an explicit 2nd CurrentDB reference such as CurrentDB.RecordsAffected (thus stepping outside the implications of the WITH clause) forces a re-evaluation of CurrentDB and that re-evaluation resets things like the .RecordsAffected property.

It would be an interesting exercise to test whether making a reference to .RecordsAffected would be dual-valued. Create an WITH block based on a DAO database variable that is a copy of CurrentDB. Do an .Execute of some action query, then assign a variable from CurrentDB.RecordsAffected, then assign a different variable from .RecordsAffected (with no prefix and still inside the WITH). Would they report different numbers, specifically with the SECOND reference that uses the WITH clause having a non-zero value? According to the VBA language reference, the explicitly qualified reference would be a different reference than the reference inside the WITH block. I'm working on another project at the moment but might get around to trying this unless someone beats me to it.
 

KitaYama

Well-known member
Local time
Tomorrow, 06:26
Joined
Jan 6, 2022
Messages
1,575
Just to repeat, RecordsAffected is a member of both QueryDef and CurrentDb objects.
Yes, it is.
But it seems that when I use QueryDef to run an action query, I can not use CurrentDB.RecordsAffected to check how many records were affected by the query.
And Visa verse.
So maybe two different instances of the same property under both.

@The_Doc_Man
I'll do the test you suggested as soon as I'm back to my PC. Thanks.
 

ebs17

Well-known member
Local time
Today, 23:26
Joined
Feb 7, 2020
Messages
1,982
I tend to encapsulate operations on their own, e.g. executing the parameter query in its own function, Keyword Factoring, Clean Code Developer. It will look like that:

Code:
    Dim db As DAO.Database
    lCount As Long

    ' sSQL as name of stored query or as sql statement
    
    Set db = CurrentDb
    lCount = ExecuteParamQuery(db, sSQL, _
                               "P1", txtManuPK, "P2", ProcessFK, "P3", cmbUser, _
                               "P4", GetCurrentDate, "P5", txtManuCount, "P6", cmbInspector)
    If lCount > 0 Then
        fltr = fltr & "," & db.OpenRecordset("select @@identity")(0)
    Else
        MsgBox "Failed"
        Exit Sub
    End If
The function for this:
Code:
Public Function ExecuteParamQuery(ByVal MyDB As DAO.Database, ByVal AnyQuery, _
                                  ParamArray QueryParams() As Variant) As Long
    Dim qd As DAO.QueryDef
    Dim i As Long

    If UBound(QueryParams) Mod 2 = 1 Then             ' pairs parameter field-value present
        If QueryExists(MyDB, AnyQuery) Then
            ' gespeicherte Abfrage
            Set qd = MyDB.QueryDefs(AnyQuery)
        Else
            ' SQL-Anweisung
            Set qd = MyDB.CreateQueryDef(vbNullString, AnyQuery)
        End If
        For i = 0 To UBound(QueryParams) Step 2
            qd.Parameters(QueryParams(i)) = QueryParams(i + 1)
        Next
        qd.Execute dbFailOnError
        ExecuteParamQuery = qd.RecordsAffected
        qd.Close
        Set qd = Nothing
    End If
End Function

Private Function QueryExists(ByVal MyDB As DAO.Database, ByVal QueryName As String) As Boolean
    Dim qd As DAO.QueryDef
    For Each qd In MyDB.QueryDefs
        If qd.Name = QueryName Then
            QueryExists = True
            Exit For
        End If
    Next
End Function

Would you test that?
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Tomorrow, 05:26
Joined
May 7, 2009
Messages
19,246
well there is already same code in the Sample database, you only need to modify it to get your RecordsAffected count.
fnAnySQL() parameter query | Access World Forums (access-programmers.co.uk)

i modify it for you:
Code:
Public Function fnAnySQL(ByVal strSQL As String, ParamArray p() As Variant)
'
' strSQL must be an Any SQL statement (Select, Insert, Update, Delete)
' parameters should be in format @1, @2, ... to be
' consistent
'
' DO NOT use parameter name which has same name as your fieldname.
' using this will take the fieldname instead of the parameter.
'
' Example:
'
' SELECT Statement:
'   Dim rs As DAO.Recordset
'   set rs = fnAnySQL("SELECT field1 FROM table1 WHERE Names = @1;", "arnelgp")
'
'
' UPDATE Statement:
'   Call fnAnySQL("Update table1 Set field1 = @1;", "arnelgp")
'
'
' DELETE Statement:
'   Call fnAnySQL("Delete table1.* From table1 Where field1 = @1;", "arnelgp")
'
' INSERT Statement:
'   Call fnAnySQL("Insert Into table1 (field1, field2) SELECT @1, @2;", "arnel", "gp")
'
'
    Dim param As Parameter
    Dim i As Integer
    With CurrentDb.CreateQueryDef("", strSQL)
        For i = 0 To .Parameters.Count - 1
            .Parameters(i) = p(i)
        Next
        If InStr(strSQL, "SELECT") = 1 And InStr(strSQL, "INTO") = 0 Then
            ' Simple select query and not Create table query
            Set fnAnySQL = .OpenRecordset(dbOpenDynaset)
        Else
            ' Action queries
            .Execute (dbFailOnError)
            fnAnySQL = .RecordsAffected
        End If
    End With
          
End Function

on your code:

Code:
Dim rec_affected as long
sql = "INSERT INTO tblManuProcess ("
sql = sql & "ManuProcessManuFK, ManuProcessProcessFK, ManuProcessUserFK, "
sql = sql & "ManuProcessDate, ManuProcessCount, ManuProcessInspector"
sql = sql & ") SELECT "
sql = sql & "P1,P2,P3,P4,P5,P6;"



rec_affected = fnAnySQL(sql, txtManuPK, ProcessFK, cmbUser, GetCurrentDate, txtManuCount, cmbInspector)

debug.print rec_affected
 
Last edited:

isladogs

MVP / VIP
Local time
Today, 22:26
Joined
Jan 14, 2017
Messages
18,261
It's curious if it's a version thing.

I realise the thread has moved on a bit but I retested my code in A2007 & A2010 & still get error 3421 if I omit the prefix in db.OpenRecordset
The code works perfectly if I include it

This is the code I used on a button click

Code:
Private Sub cmdAppend_Click()

Dim sql As String, fltr As String, db As DAO.Database, lngRec As Long

Set db = CurrentDb

sql = "INSERT INTO Table1 (N1,N2,T1,T2,D1) "
sql = sql & " VALUES ("
sql = sql & "P1,P2,P3,P4,P5"
sql = sql & ")"

With db.CreateQueryDef("", sql)
    .Parameters("P1") = N1
    .Parameters("P2") = N2
    .Parameters("P3") = T1
    .Parameters("P4") = T2
    .Parameters("P5") = D1
    .Execute dbFailOnError
    
    lngRec = .RecordsAffected
    
    If lngRec > 0 Then
       With db.OpenRecordset("select @@identity")
            fltr = fltr & "," & .Fields(0)
            MsgBox "Records added = " & lngRec
            .Close
        End With
      
     Else
        MsgBox "Failed"
        Exit Sub
     End If
    
     .Close
End With

Me.subTable1.Requery

End Sub

Can anyone see why the OpenRecordset requires the db. prefix with this particular code?
Test db attached
 

Attachments

  • RecordsAffectedTest.accdb
    408 KB · Views: 48

KitaYama

Well-known member
Local time
Tomorrow, 06:26
Joined
Jan 6, 2022
Messages
1,575
Can anyone see why the OpenRecordset requires the db. prefix with this particular code?
because if you omit db it's translated as
db.CreateQueryDef("", sql).OpenRecordset("select @@identity")

Code:
With db.CreateQueryDef("", sql)
    ......
     With .OpenRecordset("select @@identity")
       .....
    End With
     
   
End With
 

cheekybuddha

AWF VIP
Local time
Today, 22:26
Joined
Jul 21, 2014
Messages
2,321
If there is a nested WITH/END WITH block, then the outer object is also available to the inner objects
This has never been the case in my experience!

I suspect what is happening here is that the .RecordsAffected of the QueryDef.Execute is passed back to the .RecordsAffected of the Database object (CurrentDb)
 

theDBguy

I’m here to help
Staff member
Local time
Today, 14:26
Joined
Oct 29, 2018
Messages
21,536
But it seems that when I use QueryDef to run an action query, I can not use CurrentDB.RecordsAffected to check how many records were affected by the query.
Right. When you execute an action query using a QueryDef object, you'll have to use the RecordsAffected property of the QueryDef object. You can't mix the two. So:
Code:
With QueryDef
    .Execute
    .RecordsAffected
End With
With CurrentDb
    .Execute
    .RecordsAffected
End With
Cheers!
 

cheekybuddha

AWF VIP
Local time
Today, 22:26
Joined
Jul 21, 2014
Messages
2,321
Right. When you execute an action query using a QueryDef object, you'll have to use the RecordsAffected property of the QueryDef object. You can't mix the two. So:
Code:
With QueryDef
    .Execute
    .RecordsAffected
End With
With CurrentDb
    .Execute
    .RecordsAffected
End With
Cheers!
This is what I first thought when Colin said the code I posted in Post #31 didn't work, but on testing it appears that the .RecordsAffected does pass back to the Database object even after the QueryDef has gone out of scope.

See my revision of Colin's db in Post #53
 

isladogs

MVP / VIP
Local time
Today, 22:26
Joined
Jan 14, 2017
Messages
18,261
It works with my code - see attached.

You didn't check what I had written!
I thought I had used exactly what you wrote in post #31.
However, somehow, I missed that you had separated into two nested With blocks.

I agree that by doing that, it does work
Apologies
 

theDBguy

I’m here to help
Staff member
Local time
Today, 14:26
Joined
Oct 29, 2018
Messages
21,536
This is what I first thought when Colin said the code I posted in Post #31 didn't work, but on testing it appears that the .RecordsAffected does pass back to the Database object even after the QueryDef has gone out of scope.

See my revision of Colin's db in Post #53
Sorry, I didn't look at those posts. I'll check them out.
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 16:26
Joined
Feb 28, 2001
Messages
27,317
Can anyone see why the OpenRecordset requires the db. prefix with this particular code?

Since both the CurrentDB (or an object variable assigned from it) and the QueryDef object include .RecordsAffected, the question becomes "which one do you see?" for the case of WITH CurrentDB.CreateQueryDefs() - and whichever one you see, the other one would probably return zero or something else. There is a rule for OOP that, when a name reference is not qualified and a name overlap occurs, the closest object with that name is the one you see. If that is the wrong one, you need to qualify the reference.

I'm thinking that though the querydef that has a .RecordsAffected property might be "closer" in programming terms, the query was executed by ACE, which didn't execute that query in the context of the QueryDef, but rather in the context of the database. So in this case of name overlap, you have to qualify which name (property) you want. Kind of guessing here, because as previously stated, Access inner code is not visible to us.
 

Users who are viewing this thread

Top Bottom