MS Access VBA query. Select followed by a count of the result - sql

I have asked a similar question recently. However, I don't think I knew the extent of what I was wanting to accomplish with my VBA at that time. I am using Access 2010 and creating an on_click command within a form.
So my intention is to create a query, and the approach I was going to take was as follows:
varSQL2 = "SELECT * FROM Inventory WHERE Part_ID=" & rs!Part_ID & ";"
Set rs2 = db.OpenRecordset(varSQL2, dbOpenDynaset)
varStock_Level = rs2!Stock_Level +rs!Quantity
the rs!quantity and rs!Part_ID are from another query earlier in my code. stock_level and part_ID are fields in the Inventory table.
I now need to be able to create a query to count how many records came back as a result of varSQL2. so I can do something along the lines of:
varSQL2 = "SELECT * FROM Inventory WHERE Part_ID=" & rs!Part_ID & ";"
Set rs2 = db.OpenRecordset(varSQL2, dbOpenDynaset)
varStock_Level = rs2!Stock_Level +rs!Quantity
if count >1 then
....[code]....
end if
I don't really know where to start with this, slightly confused my self with subqueries. I believe I need 2 queries not one. Any help is appreciated.

There are several ways:
Create a query with COUNT function (similar as you did in refered question)
Use your query, use the rs2.movelast and read count out with rs2.RecordCount
Use domain count
Dim lngRows As Long
lngRows = DCount("*", "Inventory", "Part_ID='" & rs!Part_ID & "'")

From memory:
rs.movelast
debug.print rs.recordCount
You must always Movelast before using RecordCount. I your table/query is big, it can be time consuming.

Related

How to get result from Count Query that shows in Query Datasheet view?

I'm trying to get the result of a Count query. I need to know if it's greater than 0.
My code:
Set db = CurrentDb
Set qdg = db.QueryDefs("quyGpSumReportCount")
qdg.Sql = Replace(qdg.Sql, "plugtable", VigilTable)
qdg.Sql = Replace(qdg.Sql, "plugchurch", "'" & vChurch & "'")
Set rst = qdg.OpenRecordset("quyGpSumReportCount")
Debug.Print "Total = " & rst!Total
PartCnt = rst!Total
rst.Close
Set rst = Nothing
The query looks like this for the first church once the replacements have been made:
SELECT Count(*) AS Total
FROM (SELECT DISTINCT t.fldUserID, v.[fldChurch/Parish] FROM tblSpring2022 _
AS t INNER JOIN tblVolunteers AS v ON t.[fldUserID] = v.[ID] WHERE _
(v.[fldChurch/Parish] = '1548 Heights')) AS [%$###_Alias];
Since I don't change back to the query after running it, I can switch to the Datasheet view and see the results. In the Field Totals, there is one entry and it's value is 1. (I've run it with a couple of different churches and get different but accurate values, including 0 for a few.)
Every attempt I've made to capture the value, using the name of the field, Total, or Fields(0) or rst.Fields(0) or anything else comes up Null.
So the query is running and returning the correct result but am unable to access that result from within VBA.
OK, I didn't solve this, you guys did; but it is solved.
For reasons that entirely escape me, qdg.OpenRecordset and qdf.OpenRecordset both resulted in a Data Conversion Error. But db.OpenRecordset, suggested above, works perfectly. Not only does it run but the result of the query finds its way both into a MsgBox and into the textbox on the form, making it possible for me to use it.
If I could upvote comments I would; the solution is, after all, there.
Thanks to all!

How can I combine these two SQL queries (Access/VBA)

I am using two SQL queries in VBA that i believe they could be done in one, but I cant get it to work. I Want to turn the VBA portion into a Query outside of VBA, the VBA keeps breaking my file due to the amount of data it processes. (By break i mean it gives a message that says "this file is not a valid database" rendering the file corrupted). I search for that error but all i found was not related to breaking because of VBA code.
Anyways, here are the two queries ran with VBA.
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes ON ET.Exception = Tbl_VCodes.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
GROUP BY ET.VerintEID, Tbl_VCodes.IsApd
HAVING Tbl_VCodes.IsApd = ""OFF"";
I loop these results to update a table.
Do While Not .EOF
SQL = "UPDATE Tbl_AttendanceByAgent SET EXC = " & recSet.Fields(1).Value & _
" WHERE VerintID = '" & recSet.Fields(0).Value & "'"
CurrentDb.Execute SQL
.MoveNext
Loop
I know that i can save the results from the first query into a table and without looping I can update the main table with another SQL query, but I believe it can be done on a single SQL. I have tried using an UPDATE with a SELECT of the first query but it just errors out on me with an invalid syntax.
Yes this could be achieved in one single query as shown below
UPDATE Tbl_AttendanceByAgent
SET Tbl_AttendanceByAgent.EXC = t2.Exeptions
from Tbl_AttendanceByAgent t1
inner join (
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes as TV ON ET.Exception = TV.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
GROUP BY ET.VerintEID, TV.IsApd
HAVING Tbl_VCodes.IsApd = 'OFF'
) AS t2 on t2.EID = t1.VerintID
Note: I suppose you will replace sDate, eDate with values within your code
This question is an answer to the described errors and the given code, although it technically does not answer the request for a single SQL statement. I started adding a comment, but that's just too tedious when this answer box allows everything to be expressed efficiently at once.
First of all, referring to CurrentDb is actually NOT a basic reference to a single object instance. Rather it is more like a function call that generates a new, unique "clone" of the underlying database object. Calling it over and over again is known to produce memory leaks, and at the least is very inefficient. See MS docs for details.
Although the given code is short, it's not sweet. Not only is it repeatedly creating new database objects, it is repeatedly executing an SQL statement to update what I assume is a single row each time. That also entails regenerating the SQL string each time.
Even if executing the SQL statement repeatedly was an efficient option, there are better ways to do that, like creating a temporary (in-memory) QueryDef object with parameters. Each loop iteration then just resets the parameters and executes the same prepared SQL statement.
But in this case, it may actually be more efficient to load the table being updated into a DAO.Recordset, then use the in-memory Recordset to search for a match, then use the recordset to update the row.
I suspect that addressing a couple of those issues would make your VBA code viable.
Dim db as Database
Set db = CurrentDb 'Get just a single instance and reuse
Dim qry as QueryDef
SQL = "PARAMETERS pEXC Text ( 255 ), pID Long; " & _
" UPDATE Tbl_AttendanceByAgent SET EXC = pEXC " & _
" WHERE VerintID = pID"
set qry = db.CreateQueryDef("", SQL)
'With recSet '???
Do While Not .EOF
qry.Parameters("pEXC") = recSet.Fields(1).Value
qry.Parameters("pID") = recSet.Fields(0).Value
qry.Execute
.MoveNext
Loop
'End With recSet '???
'OR an alternative
Dim recUpdate As DAO.Recordset2
Set recUpdate = db.OpenRecordset("Tbl_AttendanceByAgent", DB_OPEN_TABLE)
Do While Not .EOF
recUpdate.FindFirst "VerintID = " & recSet.Fields(0).Value
If Not recUpdate.NoMatch Then
recUpdate.Edit
recUpdate.Fields("EXC") = recSet.Fields(1).Value
recUpdate.Update
End If
.MoveNext
Loop
I realized in commenting on Gro's answer, that the original query's aggregate clauses will produce unique values on EID, but it then becomes obvious that there is no need to group on (and sum) values which do not have Tbl_VCodes.IsApd = 'OFF'. The query would be more efficient like
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes ON ET.Exception = Tbl_VCodes.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
AND Tbl_VCodes.IsApd = 'OFF'
GROUP BY ET.VerintEID;
BTW, you could consider implementing the same temporary QueryDef pattern as I showed above, then you'd change the first WHERE expression to something like
PARAMETERS PsDate DateTime, PeDate DateTime;
...
WHERE (ET.ExceptDate Between [PsDate] And [PeDate])
...

MS ACCESS VBA: type mismatch error in recordset name into SQL statement string

How should I write the name of the recordset correctly? I have a type mismatch error on & rsg & at "sq3=..." line. Thanks in advance.
Dim rsG As DAO.Recordset
Dim sq2, sq3 As String
sq2 = "SELECT * from GeneralTable "
sq2 = sq2 & "where gsede='LION' and (gExclu is null) and (gAda is null) "
sq2 = sq2 & "order by gnomb,gnif;"
Set rsG = CurrentDb.OpenRecordset(sq2)
sq3 = "UPDATE " & rsG & " SET gsede ='AA' WHERE gsede='LION'"
DoCmd.RunSQL (sq3)
You are mixing up totally different ways to update data - UPDATE SQL and VBA recordset.
If you want to update a recordset row by row, you do something like
Do While Not rsG.EOF
If rsG!foo = "bar" Then
rsG.Edit
rsG!gsede = "AA"
rsG.Update
End If
rsG.MoveNext
Loop
Do the update using a single query:
update generaltable
set gsede = 'AA'
where gsede ='LION' and (gExclu is null) and (gAda is null);
If you do the update off of rsG, then you'll likely do a separate update for each row, and that is inefficient.
You can't mix and match SQL and recordset objects. You can either build a full update statement that includes the logic of the first select (as other answer suggests), or you can open a dynamic recordset and loop through the records programmatically to make the updates.
That being said, looping through large recordsets programmatically to make updates is usually better handled by a bulk SQL statement, unless it is essential to consider each row individually inside the code - which in your basic example would not be the case.
MS has a good article on the DAO.Recordset object. There is a dynamic-type Recordset example about halfway down.

Recordset.Edit or Update sql vba statement fastest way to update?

I recently came across vba update statements and I have been using Recordset.Edit and Recordset.Update to not only edit my existing data but to update it.
I want to know the difference between the two: recordset.update and Update sql Vba statement. I think they all do the same but I can't figure which one is more efficient and why.
Example code below:
'this is with sql update statement
dim someVar as string, anotherVar as String, cn As New ADODB.Connection
someVar = "someVar"
anotherVar = "anotherVar"
sqlS = "Update tableOfRec set columna = " &_
someVar & ", colunmb = " & anotherVar &_
" where columnc = 20";
cn.Execute stSQL
This is for recordset (update and Edit):
dim thisVar as String, someOthVar as String, rs as recordset
thisVar = "thisVar"
someOthVar = "someOtherVar"
set rs = currentDb.openRecordset("select columna, columnb where columnc = 20")
do While not rs.EOF
rs.Edit
rs!columna = thisVar
rs!columnb = someOthvar
rs.update
rs.MoveNext
loop
Assuming WHERE columnc = 20 selects 1000+ rows, as you mentioned in a comment, executing that UPDATE statement should be noticeably faster than looping through a recordset and updating its rows one at a time.
The latter strategy is a RBAR (Row By Agonizing Row) approach. The first strategy, executing a single (valid) UPDATE, is a "set-based" approach. In general, set-based trumps RBAR with respect to performance.
However your 2 examples raise other issues. My first suggestion would be to use DAO instead of ADO to execute your UPDATE:
CurrentDb.Execute stSQL, dbFailonError
Whichever of those strategies you choose, make sure columnc is indexed.
The SQL method is usually the fastest for bulk updates, but syntax is often clumsy.
The VBA method, however, has the distinct advantages, that code is cleaner, and the recordset can be used before or after the update/edit without requering the data. This can make a huge difference if you have to do long-winded calculations between updates. Also, the recordset can be passed ByRef to supporting functions or further processing.
I have found that when I need to update every record in a table in order, such as adding a sequential ID when using Autonumber is not feasible, adding a running total, or any calculation that is incremental based on some value in the recordset, that the DAO method is much faster.
If your data is not in the order you need it processed in, and you instead need to rely on matching values to the data source, then SQL is much more efficient.
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("select invoice_num from dbo_doc_flow_data where barcode = '" & Me.barcode_f & "'")
Do While Not rs.EOF
rs.Edit
rs!invoice_num = Me!invoice_num_f
rs.Update
rs.MoveNext
Loop
rs.Close

SQL query various ID's at once

I need to query an XLS "database" from within an Excel Workbook via ADO.
I am using the following:
...code
objRecordset.Open "SELECT * FROM [MY_TABLE$] WHERE Code_ID = " & the_ID & ", objConnection, adOpenStatic, adLockOptimistic, adCmdText
...code
It runs well if I am only searching for an Id (the_ID), eg 1234
But what I need is to search for various the_ID's at the same time....
So for example any matches of ID's 1234, 1225, 6225, 5656 should return on the query.
So more or less an array of Id's.
Any help is appreciated...
You can join the array of ids using Join then use IN in the sql, e.g.
Dim ids(3) As String
ids(0) = "1234"
ids(1) = "1225"
ids(2) = "6225"
Dim sql As String
sql = "SELECT * FROM [MY_TABLE$] WHERE Code_ID IN (" & Join(ids, ",") & ")"
If you are getting your ids from a range then this answer will be of interest to you.
Note
Since you are only querying your own spreadsheet I have assumed security may not be a major concern, however I would normally recommend using parameterised queries, this would either require a known number of ids, or you would have to generate the sql on the fly, something like:
WHERE Code_id IN (#Param1, #Param2, #Param3, #Param4)
It should not be too hard to make your sql like this then add your ids as parameters to the recordset. I haven't used VBA in a long time though, so I can't quite recall the right way to add the parameters (or if it is even possible). If I remember I will update the answer.