VBA Access - Multiple Tables count by date - ms-access

We'd like to count from an Access database that has multiple tables - about 50.
We need to count from 1 column in each table that is 'QCPASS' This is a check box - if a product passed the box was checked if failed then not. We need to count both for EACH table, also allowing the user to specify a date range from a date column that exists in every table.
I've tried this with a query but I am told the query is unable to select, count and do the date range. Any VBA help would be great.
Exporting to Excel would be great, but any results would be fine. Here is the query I created that counts in a column from each table passes and failures. I can't iterate with a query either, so VBA seems the way to go:
SELECT "Table1" , Count('qcpass') AS column
FROM 5000028
GROUP BY [5000028].qcpass
union
SELECT "Table2",count('qcpass')
FROM 5000029
Group By [5000029].qcpass;

You can traverse the full TableDefs collection in your database, and create a query using VBA.
A word of warning: The TableDefs collection has the Access database system tables, so you need to skip this. A way I suggest you is to check for a specific table name prefix (it is noted in the code below).
public sub createMyBigUnionQuery()
dim db as DAO.database(), tbl as DAO.tableDef
dim strSQL as string, i as integer
set db = currentdb()
i = 1
for each tbl in db.TableDefs
if left(tbl.name, 1) = "5" then ' Check for a table name prefix
if i = 1 then
' The final spaces are important
strSQL = "select '" & tbl.Name & "' as table, count(qcpass) as column " & _
"from [" & tbl.Name & "] " & _
"group by qcpass "
else
' The final spaces are important
strSQL = strSQL & " union all " & _
"select '" & tbl.Name & "' as table, count(qcpass) as column " & _
"from [" & tbl.Name & "] " & _
"group by qcpass "
end if
i = i + 1
end if
next tbl
db.createQueryDef "qryYourFinalQuery", strSQL
db.close
exit sub
Notice that you can define any valid query you want. Take this as a hint, and tweak it to fit your specific needs.
Hope this helps you
Adressing #HansUp comment, if you need to filter your data by date, you have two options:
Include the where condition on every select created by the procedure
Include the date field in your query and group by it, and create a second query to filter the data you need from the created query.
I would personally go with option 1, and here is a sample code:
public sub createMyBigUnionQueryWithDates(d0 as date, d1 as date)
dim db as DAO.database(), tbl as DAO.tableDef
dim strSQL as string, i as integer
set db = currentdb()
i = 1
for each tbl in db.TableDefs
if left(tbl.name, 1) = "5" then ' Check for a table name prefix
if i = 1 then
' The final spaces are important
strSQL = "select '" & tbl.Name & "' as table, count(qcpass) as column " & _
"from [" & tbl.Name & "] " & _
"where rowDate between " & cDbl(d0) & " and " &cDbl(d1) & " " & _
"group by qcpass "
else
' The final spaces are important
strSQL = strSQL & " union all " & _
"select '" & tbl.Name & "' as table, count(qcpass) as column " & _
"from [" & tbl.Name & "] " & _
"where rowDate between " & cDbl(d0) & " and " &cDbl(d1) & " " & _
"group by qcpass "
end if
i = i + 1
end if
next tbl
db.createQueryDef "qryYourOtherFinalQuery", strSQL
db.close
exit sub
The reason I use cDbl(d0) is because Access dates are sensitive to regional settings, and I've had a lot of headaches dealing with it. Access (and many other Microsoft products) store dates as floating-point numbers (the integer part is the date, and the decimal part is the time).
Another word of warning: If your dates don't include time, then the between condition will work. But if they do include time, then I recommend you change the where condition to this:
"where rowDate >= " & cDbl(d0) & " and rowDate < " & cDbl(d1 + 1)"

Related

Selecting fields command gives error

I want to sort multiple fields in an access data base but being a newbie I took reference from MSDN program and first trying to sort on a single field. I'm getting error From syntax wrong on the following lines.
Set rst = dbs.OpenRecordset("SELECT ACT_CD, " _
& "SNO FROM Banks_Trnx_2018-2019" _
& "ORDER BY SNO DESC;")
The full program as follows.
Option Compare Database
Sub OrderByX()
Dim dbs As Database, rst As Recordset
' Modify this line to include the path to Northwind
' on your computer.
Set dbs = OpenDatabase("E:\FY_2018-2019\Banks\Banks_Trnx_2018-2019.accdb")
' Select the last name and first name values from
' the Employees table, and sort them in descending
' order.
Set rst = dbs.OpenRecordset("SELECT ACT_CD, " _
& "SNO FROM Banks_Trnx_2018-2019" _
& "ORDER BY SNO DESC;")
' Populate the Recordset.
rst.MoveLast
' Call EnumFields to print recordset contents.
' EnumFields rst, 12
dbs.Close
End Sub
I am clueless. Please help. As already mentioned I would like to sort on three fields (multiple fields) comprising of text and numerical fields which presently I can easily do by setting columns in proper order and selecting them together while sorting. Since it is a repetitive operation I am trying to develop VBA procedure for the same. Any pointer in the right direction shall be highly appreciated.
You should set your sql to a variable first - makes it a lot easier to troubleshoot - you'd be able to see you've left out some spaces between keywords and table
Set rst = dbs.OpenRecordset("SELECT ACT_CD, " _
& "SNO FROM [Banks_Trnx_2018-2019]" _
& "ORDER BY SNO DESC;")
Should be (with added brackets for table as suggested by #skkakkar)
Set rst = dbs.OpenRecordset("SELECT ACT_CD, " _
& "SNO FROM [Banks_Trnx_2018-2019] " _
& "ORDER BY SNO DESC;")
You have a - in your table name. I didn't even think that was allowed, either put brackets around your table name or change the table name.
"SELECT ACT_CD, " _
& "SNO FROM [Banks_Trnx_2018-2019]" _
& "ORDER BY SNO DESC;"
or
"SELECT ACT_CD, " _
& "SNO FROM [Banks_Trnx_2018_2019] " _
& "ORDER BY SNO DESC;"
and change table name.

Use text from record to paste into an Access form controlbox

Based on a user's job ID number, I create a recordset of an ID with its different unit types (think pipe sizes) and unit (think footage of pipe). Each unit type record already has the name of the form textbox where the total footage goes in a different column. What I want to do is go through each recordset and plugin the footage for each unit type for that job ID number (that the user puts in a form).
Dim rst_UnitEntryCounts As Recordset
Set rst_UnitEntryCounts = CurrentDb.OpenRecordset("SELECT tbl_UnitTypes.UnitTypes_WeeklyTextBoxUserEntryForm, " _
& " tbl_PMEntry.PMEntry_Week_Ending, " _
& " tbl_UnitTypes.UnitTypes_CumalativeTextBoxUserEntryForm, " _
& " Sum(tbl_UnitEntry.UnitEntry_Unit_Count) AS SumOfUnitEntry_Unit_Count " _
& "FROM tbl_UnitTypes " _
& " INNER JOIN tbl_UnitEntry ON tbl_UnitTypes.UnitTypes_ID = tbl_UnitEntry.UnitEntry_UnitTypes_ID) " _
& " INNER JOIN (tbl_PMHeader " _
& " INNER JOIN tbl_PMEntry ON tbl_PMHeader.PMHeader_ID = tbl_PMEntry.PMEntry_PMHeader_ID) ON tbl_UnitEntry.UnitEntry_PMEntry_ID = tbl_PMEntry.PMEntry_PMHeader_ID " _
& "WHERE tbl_PMHeader.PMHeader_ID = " & num_PM _
& "GROUP BY tbl_UnitTypes.UnitTypes_WeeklyTextBoxUserEntryForm, " _
& " tbl_PMEntry.PMEntry_Week_Ending, " _
& " tbl_UnitTypes.UnitTypes_CumalativeTextBoxUserEntryForm ")
rst_UnitEntryCounts.MoveFirst
Do Until rst_UnitEntryCounts.EOF = True
[rst_UnitEntryCounts.UnitTypes_WeeklyTextBoxUserEntryForm] = SumOfUnitEntry_Unit_Count
rst_UnitEntryCounts.MoveNext
Loop
Exit Sub
image of what my query table looks like
Also, Im getting an error 3131 Syntax error in FROM clause as well.
Thanks in advance!
Finally figured out how to "place" a value (sum of units) in a textbox that varies based on criteria (unit type) in a form, where the name of the textbox is inside a record itself within my recordset. In case someone else has a similar question, here is how I did it:
Dim cntl As String
Dim frm As String
Dim rst_UnitEntry As Recordset
frm = "frm_UserEntry"
Set rst_UnitEntryCounts = CurrentDb.OpenRecordset("SELECT...)
If rst_UnitEntryCounts.RecordCount <> 0 Then
rst_UnitEntryCounts.MoveFirst
Do Until rst_UnitEntryCounts.EOF = True
If rst_UnitEntryCounts![UnitTypes_CumalativeTextBoxUserEntryForm] <> "" Then
cntl = rst_UnitEntryCounts![UnitTypes_CumalativeTextBoxUserEntryForm]
Forms(frm).Controls(cntl) = rst_UnitEntryCounts![SumOfUnitEntry_Unit_Count]
End If
rst_UnitEntryCounts.MoveNext
Loop

Why doesn't this DAO recordset query return any results?

I am working on an Access 2007 database and I have set up a DAO recordset query, however, it is not returning any results. It does not throw up any errors, just no results. Me.ProID is a text field, it includes letters and numbers. I think it has something to do with the text field, maybe the placement of quotations.
Dim contractSQL As String
Dim contractDB As DAO.Database
Dim contractRS As DAO.Recordset
contractSQL = "SELECT Top 1 ContractName, ItemDescription, Price FROM ContractPricing WHERE AccountNo = " & Me.Parent.[AccntNumber] & " AND PartNo = " & """ & Me.ProdID & """ & " ORDER BY Price Asc"
Set contractDB = CurrentDb
Set contractRS = contractDB.OpenRecordset(contractSQL)
Debug.Print contractRS.RecordCount
That string concatenation is likely wrong. " & """ & Me.ProdID & """ & " should almost certainly be '" & Me.ProdID & "'
I don't know if you're referring to this:
contractSQL = "SELECT Top 1 ContractName, ItemDescription, Price FROM ContractPricing WHERE AccountNo = " & Me.Parent.[AccntNumber] & " AND PartNo = " & Me.ProdID & " ORDER BY Price Asc"
I'm not sure if this would help you. I'm also new to this. But hopefully it will.

Adding values into a table with a query

So all I'd like to do is add two queries into a table called tmpGroupSearch. I'm not sure how to do it wit out creating a record set and looping through every record and individually adding them in. I'm thinking there is a much easier way to do this I just can't find the proper structure.
Here are my queries:
"SELECT tblGroupHeader.GroupName" _
& ", tblGroupHeader.GroupNum" _
& ", tblAlsoKnown.AlsoKnown" _
& " FROM tblGroupHeader" _
& " LEFT JOIN tblAlsoKnown ON tblGroupHeader.GroupNum = tblAlsoKnown.GroupNum" _
& " WHERE tblGroupHeader.GroupName like '" & txtgroupSearch.Value & "*'" _
& " OR tblGroupHeader.GroupNum like '" & txtgroupSearch.Value & "*';"
"Select * FROM tblActionLog WHERE AlsoKnown LIKE '" & txtgroupSearch.Value & "*';"
You can follow an INSERT INTO clause with a list of values, like #Asad shows, or also a SELECT query. Do yourself a favor and always list the field names in your SQL or you are just creating time bombs for the future.
Dim strSelect1 As String
Dim strSelect2 As String
strSelect1 = "SELECT tblGroupHeader.GroupName" _
& ", tblGroupHeader.GroupNum" _
& ", tblAlsoKnown.AlsoKnown" _
& " FROM tblGroupHeader" _
& " LEFT JOIN tblAlsoKnown ON tblGroupHeader.GroupNum = tblAlsoKnown.GroupNum" _
& " WHERE tblGroupHeader.GroupName like '" & txtgroupSearch.Value & "*'" _
& " OR tblGroupHeader.GroupNum like '" & txtgroupSearch.Value & "*';"
strSelect2 = "Select * FROM tblActionLog WHERE AlsoKnown LIKE '" & txtgroupSearch.Value & "*';"
Dim strInsert1 As String
Dim strInsert2 As String
strInsert1 = "INSERT INTO tmpGroupSearch (GroupName, GroupNum, AlsoKnown) " & strSelect1
'the next version is valid SQL, but *dangerous* because field names are not enumerated
strInsert2 = "INSERT INTO tmpGroupSearch " & strSelect2
It is possible to write the INSERT INTO statement in two forms.
The first form does not specify the column names where the data will be inserted, only their values:
INSERT INTO table_name
VALUES (value1,value2,value3,...);
The second form specifies both the column names and the values to be inserted:
INSERT INTO table_name (column1,column2,column3,...)
VALUES (value1,value2,value3,...);
Test this parameter query in the Access query designer and adjust as needed so that it returns the row set you expect for the pSearchText parameter value you supply.
PARAMETERS pSearchText Text ( 255 );
SELECT gh.GroupName, gh.GroupNum, ak.AlsoKnown
FROM
tblGroupHeader AS gh
LEFT JOIN tblAlsoKnown AS ak
ON gh.GroupNum = ak.GroupNum
WHERE
gh.GroupName Like "'" & pSearchText & "*'"
OR gh.GroupNum Like "'" & pSearchText & "*'"
UNION ALL
SELECT al.GroupName, al.GroupNum, al.AlsoKnown
FROM tblActionLog AS al
WHERE al.AlsoKnown Like "'" & pSearchText & "*'"
Once you have it returning the correct data, convert it to an INSERT query by changing the beginning of the statement to this ...
PARAMETERS pSearchText Text ( 255 );
INSERT INTO tmpGroupSearch (GroupName, GroupNum, AlsoKnown)
SELECT gh.GroupName, gh.GroupNum, ak.AlsoKnown
... and the rest
Save that query as qryGroupSearchAppend. Then you can execute that query from your VBA code:
Dim qdf As DAO.QueryDef
Set qdf = CurrentDb.QueryDefs("qryGroupSearchAppend")
qdf.Parameters("pSearchText").Value = Me.txtgroupSearch.Value
qdf.Execute dbFailOnError

Fastest way of looping in recordset

I have a VB6 application. I have 2 recordsets having the same number of records (almost 50k). Now I have to loop 50k*50k *(number of fields). Can any one tell me the fastest way to do so?
Thanks in advance.
As others have pointed out it is not a good idea to do this client side but here are a few pointers for speeding up recordset access.
Open the recordset as forward only (adOpenForwardOnly) and if you are not writing then read only also.
Refer to the fields by number so instead of doing rs.Fields(“MyField”) you would use rs.Fields(0)
If you are writing back to the database then consider wrapping things up in a transaction to speed things up.
When looping use “Do until rs.EOF” instead of counting records.
Thats all I can think of for now but they should help a bit
There really is no fastest way to do this in vb6.
You'd use 2 for loops.
You might want to give us more details as to why you are pulling down 50k records (twice) and comparing each field. This is usually an indication that something needs to be done on the database side but was brought in to the client side to be solved.
If your two databases are the same and the only difference is the data, you could do something like this (pseudo-code):
SELECT t1.A, t2.A, t2.B, t2.B, ...
FROM t1
INNER JOIN t2 on t1.id = t2.id
WHERE (t1.A <> t2.A) OR (t1.B <> t2.B) OR ...
t1 and t2 would be your two tables. This isn't the most efficient but it would allow you to do the comparisons very easily. Also, you could get more complicated with what you show in the SELECT statement. Currently it is just a listing of both columns side by side.
Having clarified that you've got two Access databases you need to compare, the easiest is to link the two together and then compare with a query.
Right-click the white empty space, Link tables.
Locate you second DB, select the table in it.
Now you can write a query to compare the two. The data being compared is always up to date as it's being pulled through the link.
I have written code to compare and synchronize two data tables many, many times, and I've posted about it here on SO. The key is to use SQL to limit the results to the records that don't match, but the chief insight is to write your on-the-fly SQL ONE COLUMN AT A TIME. With a WHERE clause on each of your SQL statements, this will be much more efficient than any other comparison method.
Within Access, I've written this code to update one table from another. It assumes the tables have the same fields and that there's a PK field that actually identifies the same record in both tables. The code has a dependency on my SQLRun() function to actually execute the SQL, but that line could be replaced with CurrentDB.Execute if you don't want that.
Public Function UpdateTableData(ByVal strSourceTable As String, _
ByVal strTargetTable As String, ByVal strJoinField As String, _
ByRef db As DAO.Database, Optional ByVal strExcludeFieldsList As String, _
Optional ByVal strUpdatedBy As String = "Auto Update", _
Optional strAdditionalCriteria As String) As Boolean
Dim strUpdate As String
Dim rsFields As DAO.Recordset
Dim fld As DAO.Field
Dim strFieldName As String
Dim strNZValue As String
Dim strSet As String
Dim strWhere As String
strUpdate = "UPDATE " & strTargetTable & " INNER JOIN " & strSourceTable _
& " ON " & strTargetTable & "." & strJoinField & " = " _
& strSourceTable & "." & strJoinField
' if the fields don't have the same names in both tables,
' create a query that aliases the fields to have the names of the
' target table
' if the source table is in a different database and you don't
' want to create a linked table, create a query and specify
' the external database as the source of the table
' alternatively, for strTargetTable, supply a SQL string with
' the external connect string
Set rsFields = db.OpenRecordset(strSourceTable)
For Each fld In rsFields.Fields
strFieldName = fld.Name
If strFieldName <> strJoinField Or (InStr(", " & strExcludeFieldsList _
& ",", strFieldName & ",") <> 0) Then
Select Case fld.Type
Case dbText, dbMemo
strNZValue = "''"
Case Else
strNZValue = "0"
End Select
strSet = " SET " & strTargetTable & "." & strFieldName & " = _
varZLSToNull(" & strSourceTable & "." & strFieldName & ")"
strSet = strSet & ", " & strTargetTable & ".Updated = #" & Date & "#"
strSet = strSet & ", " & strTargetTable & ".UpdatedBy = " _
& STR_QUOTE & strUpdatedBy & STR_QUOTE
strWhere = " WHERE Nz(" & strTargetTable & "." & strFieldName & ", " _
& strNZValue & ") <> Nz(" & strSourceTable & "." & strFieldName _
& ", " & strNZValue & ")"
If db.TableDefs(strTargetTable).Fields(fld.Name).Required Then
strWhere = strWhere & " AND " & strSourceTable & "." _
& strFieldName & " Is Not Null"
End If
If Len(strAdditionalCriteria) > 0 Then
strWhere = strWhere & " AND " & strAdditionalCriteria
End If
Debug.Print strUpdate & strSet & strWhere
Debug.Print SQLRun(strUpdate & strSet & strWhere, dbLocal) & " " _
& strFieldName & " updated."
End If
Next fld
Debug.Print dbLocal.OpenRecordset("SELECT COUNT(*) FROM " _
& strTargetTable & " WHERE Updated=#" & Date & "# AND UpdatedBy=" _
& STR_QUOTE & strUpdatedBy & STR_QUOTE)(0) _
& " total records updated in " & strTargetTable
rsFields.Close
Set rsFields = Nothing
UpdateTableData = True
End Function
try to use the algorithm of sql., the left and right join., then apply it in vb..,
i also have the same problem with you but i try that solution and it works.., on first, it takes almost 3 hours the complete the query but when i apply the sql algo, it just takes few minis