Running two VBA codes on same Excel breaks - ms-access

I am using VBA in Access to modify a excel sheet using Macro 1 and input it in a table using Macro 2. When I run both of these consecutively, my system gets stuck in a loop, but works fine if I restart the Microsoft access application after running one Macro and run Macro 2. Also at times, the excel file on which I am running my code gets a pop-up box to enable read/write access.. Can someone help me with it?
Macro1
Function Clean()
Dim CurrFilePath, PathName, Week As String
Dim Filename
Dim OpenExcel As Object
Set OpenExcel = CreateObject("Excel.Application")
OpenExcel.Visible = False
Dim OpenWorkbook, WS As Object
Dim i, j As Integer
Dim Count_WS As Integer
OpenExcel.Quit
CurrFilePath = Application.CurrentProject.path
StartTime = Timer
Week = InputBox("Enter the week for the data import e.g. 34")
PathName = CurrFilePath & "\Direct Deliveries\Week " & Week & "\"
Example = CurrFilePath & "\Direct Deliveries\Week " & Week
Confirm:
Confirm_Folder = MsgBox("Does the Direct Deliveries info exist in " & PathName & " path", vbYesNo)
If Confirm_Folder = vbNo Then
path = InputBox("Locate Direct Deliveries .xlsx on your System and Copy the Dir path here e.g." & Example)
PathName = path & "\"
GoTo Confirm
End If
Filename = Dir(PathName & "*.xlsx")
Do While Len(Filename) > 0
Set OpenExcel = CreateObject("Excel.Application")
OpenExcel.Visible = False
OpenExcel.EnableEvents = False
OpenExcel.ScreenUpdating = False
'Variables to track first cell
i = 0
j = 0
PathFile = PathName & Filename
Set OpenWorkbook = OpenExcel.Workbooks.Open(PathFile)
For Each WS In OpenWorkbook.Worksheets
'If condition to check correct worksheets
On Error Resume Next
If Range("A1").Value = "Carrier SCAC" And Range("D1").Value = "Trip ID" Then
'Loop to fill blank TripIDs
For Each Cell In WS.UsedRange.Columns(4).Cells
' For blank cells, set them to equal the cell above
If WS.Cells(Cell.Row, 1) <> "ABCD" And Not IsEmpty(WS.Cells(Cell.Row, 9)) Then
If i <> 0 Then
If (Len(Cell.Text) = 0) And PreviousCell <> "Trip ID" And Cell.Row Then
Cell.Value = PreviousCell
End If
End If
PreviousCell = Cell
i = i + 1
End If
Next Cell
'Loop to fill blank SCAC Codes
For Each CarrierCell In WS.UsedRange.Columns(1).Cells
' For blank cells, set them to equal the cell above
If j <> 0 Then
If (Len(CarrierCell.Text) = 0) And PreviousCell <> "Carrier SCAC" And PreviousCell <> "ABCD" And Not IsEmpty(WS.Cells(CarrierCell.Row, 4)) Then
CarrierCell.Value = PreviousCell
End If
End If
PreviousCell = CarrierCell
j = j + 1
Next CarrierCell
End If
Count_WS = Count_WS + 1
Next WS
Filename = Dir()
OpenWorkbook.Close SaveChanges:=True
Set OpenWorkbook = Nothing
OpenExcel.Quit
Set OpenExcel = Nothing
Loop
'Display the end status
TotalTime = Format((Timer - StartTime) / 86400, "hh:mm:ss")
Application.Echo True
DeleteImportErrTables
End Function
Macro 2
'--------------------------------------------------------
' Author: Akanksha Goel
' The code imports Direct Deliveries erroneous excel templates to Access Database
'------------------------------------------------------------
'
'------------------------------------------------------------
Function ListErrBeforeImports()
Dim OpenExcel As Object
Set OpenExcel = CreateObject("Excel.Application")
OpenExcel.Visible = False
Dim PathFile As String, Filename As String, PathName As String
Dim TableName As String
Dim HasFieldNames As Boolean
Dim OpenWorkbookED As Object
Dim SQL, CurrFilePath As String
Dim SQLcreate, SQLAlter, SQLSet As String
Dim SQL2, SQL3 As String
Dim Count_Templates As Integer
StartTime = Timer
OpenExcel.Quit
'Turn Off the warnings and screen updating
DoCmd.SetWarnings False
Application.Echo False
OpenExcel.EnableEvents = False
OpenExcel.ScreenUpdating = False
CurrFilePath = Application.CurrentProject.path
Week = InputBox("Enter the week for the data import e.g. 34")
PathName = CurrFilePath & "\Direct Deliveries\Week " & Week & "\"
Example = CurrFilePath & "\Direct Deliveries\Week " & Week
Confirm:
Confirm_Folder = MsgBox("Does the Direct Deliveries info exist in " & PathName & " path", vbYesNo)
If Confirm_Folder = vbNo Then
path = InputBox("Locate Direct Deliveries .xlsx on your System and Copy the Dir path here e.g." & Example)
PathName = path & "\"
GoTo Confirm
End If
HasFieldNames = True
TableName = "TempTable"
Filename = Dir(PathName & "*.xlsx")
PathFile = PathName & Filename
'Arguments for function AssignTablesToGroup()
Dim Arg1 As String
Dim Arg2 As Integer
Arg1 = "EmptyDeliveryDates_TripsWeek" & Week
Call DeleteTable(Arg1)
Arg2 = 383
SQLcreate = "Create Table EmptyDeliveryDates_TripsWeek" & Week & " ( TripID Text, ShipToZip Text, ArriveDelivery Text, Carrier Text, SourceWorkbook Text);"
DoCmd.RunSQL SQLcreate
'Assign Error Table to 'Errors in DirectDeliveries Excels' Group
Call AssignToGroup(Arg1, Arg2)
'Arguments for function AssignTablesToGroup()
Dim Arg3 As String
Arg3 = "InvalidZip_TripsWeek" & Week
DeleteTable Arg3
Arg2 = 383
SQLcreate = "Create Table InvalidZip_TripsWeek" & Week & " ( TripID Text, ShipToZip Text, ArriveDelivery Text, Carrier Text, SourceWorkbook Text);"
DoCmd.RunSQL SQLcreate
'Assign Error Table to 'Errors in DirectDeliveries Excels' Group
Call AssignToGroup(Arg3, Arg2)
'Arguments for function AssignTablesToGroup()
Dim Arg4 As String
Arg4 = "InvalidTrip_TripsWeek" & Week
DeleteTable Arg4
Arg2 = 383
SQLcreate = "Create Table InvalidTrip_TripsWeek" & Week & " ( TripID Text, ShipToZip Text, ArriveDelivery Text, Carrier Text, SourceWorkbook Text);"
DoCmd.RunSQL SQLcreate
'Assign Error Table to 'Errors in DirectDeliveries Excels' Group
Call AssignToGroup(Arg4, Arg2)
Do While Len(Filename) > 0
Set OpenExcel = CreateObject("Excel.Application")
OpenExcel.Visible = False
OpenExcel.EnableEvents = False
OpenExcel.ScreenUpdating = False
PathFile = PathName & Filename
Set OpenWorkbookED = OpenExcel.Workbooks.Open(PathFile, ReadOnly)
Set WS_Book = OpenWorkbookED.Worksheets
DeleteTable "TempTable"
'Loop through Worksheets in each template workbook
For Each WS In WS_Book
WorksheetName = WS.Name
x = WS.Range("A1")
If WS.Range("A1") = "Carrier SCAC" Then
'Get the used records in worksheet
GetUsedRange = WS.UsedRange.Address(0, 0)
'Import records from worksheet into Access Database table
DoCmd.TransferSpreadsheet acImport, 10, "TempTable", PathFile, HasFieldNames, WorksheetName & "!" & GetUsedRange
SQLAlter = "ALTER TABLE TempTable ADD COLUMN SourceBook TEXT(100)"
DoCmd.RunSQL SQLAlter
SQLSet = "UPDATE TempTable SET TempTable.SourceBook = '" & Filename & "' where ([Arrive Delivery]) is NULL or len([Arrive Delivery])<2 or len([Trip ID])<8 or len([Ship to Zip])<5;"
DoCmd.RunSQL SQLSet
SQL = "INSERT INTO " & Arg4 & "(TripID, ShipToZip, ArriveDelivery, Carrier, SourceWorkbook) Select Distinct [Trip ID], [Ship to Zip], [Arrive Delivery], [Carrier SCAC], SourceBook FROM TempTable WHERE len([Trip ID])<8 and len([Ship To Zip])>0 and len([Arrive Delivery])>0;"
DoCmd.RunSQL SQL
SQL2 = "INSERT INTO " & Arg3 & "(TripID, ShipToZip, ArriveDelivery, Carrier, SourceWorkbook) Select Distinct [Trip ID], [Ship to Zip], [Arrive Delivery], [Carrier SCAC], SourceBook FROM TempTable WHERE len([Ship To Zip])<5 and len([Arrive Delivery])>0 and len([Trip ID])>0;"
DoCmd.RunSQL SQL2
SQL3 = "INSERT INTO " & Arg1 & "(TripID, ShipToZip, ArriveDelivery, Carrier, SourceWorkbook) Select Distinct [Trip ID], [Ship to Zip], [Arrive Delivery], [Carrier SCAC], SourceBook FROM TempTable WHERE ([Arrive Delivery] is NULL or len([Arrive Delivery])<2) and len([Ship To Zip])>0 and len([Trip ID])>0 ;"
DoCmd.RunSQL SQL3
DoCmd.DeleteObject acTable, "TempTable"
Count_Templates = Count_Templates + 1
End If
Next WS
OpenWorkbookED.Saved = True
OpenWorkbookED.Close
Filename = Dir()
Set OpenWorkbookED = Nothing
OpenExcel.Quit
Set OpenExcel = Nothing
Loop
'Display the end status
TotalTime = Format((Timer - StartTime) / 86400, "hh:mm:ss")
MsgBox "Done! Error tables updated in 'Errors in DirectDeliveries Excels' group in with " & Count_Templates & " Templates " & TotalTime & " minutes", vbInformation
Application.Echo True
'CallFunction Delete Import Tables
DeleteImportErrTables
End Function

Merge the two functions so you only open one instance (your OpenExcel object) of Excel.

Related

How do I reformat all Word Form date fields from m/d/yyyy to yyyy-mm-dd using VBA?

I am working on a custom VBA script that dynamically collects user-entered form data and inserts it into a MySQL database. My problem is, to convert form field data into an SQL script, I have to use string functions; thus, all my data, including dates, gets inserted as text. I need to convert all the dates the form collects from m/d/yyyy format to yyyy-mm-dd format for my MySQL database to infer schema and load date data into DB without an error. I need to do so dynamically, meaning, the script has to work regardless of how many date fields are collected. I have:
Private Sub Submit_Button()
Dim doc as Document
Dim control As ContentControl
Dim FormDateField As Date
Dim ReportNumber As String
Dim myValues As String
Dim myFields As String
Dim conn As ADODB.Connection
Dim strSQL As String
Set doc = Application.ActiveDocument
Set conn = New ADODB.Connection
conn.open "DSN=ABCD"
For Each control In doc.ContentControls
Skip = False
If Left(control.Range.Text, 5) = "Click" Or Left(control.Range.Text, 6) = "Choose" Then
Skip = True
Else:
myFields = myFields & control.Tag
myValues = myValues & "'" & control.Range.Text & "'"
End If
If Not Skip Then
myFields = myFields & ", "
myValues = myValues & ", "
End If
Next
myFields = Left(myFields, Len(myFields) - 2)
myValues = Left(myValues, Len(myValues) - 2)
strSQL = "INSERT INTO TABLE_1 ("
strSQL = strSQL & myFields
strSQL = strSQL & ") VALUES (" & myValues
strSQL = strSQL & ")"
conn.Execute strSQL
MsgBox "Form data saved to database!"
conn.Close
End Sub
However, my program is crashing because it is trying to insert a string into the date field (the actual final form will have many date fields.) I thought if I change the date format to MySQL format, it may be able to infer schema? I tried adding
If IsDate(control.Range.Text) Then
control.Range.Text = Format(control.Range.Text, "yyyy-mm-dd")
Else FoundOne = False
End If
and I know in Excel you can do:
Application.FindFormat.NumberFormat = "m/d/yyyy"
Application.ReplaceFormat = "yyyy-mm-dd"
Any suggestions? Thank you.
Assuming all dates are in date-picker content controls, you could use:
Private Sub Submit_Button()
Dim CCtrl As ContentControl, bSv As Boolean, DtFmt As String
Dim myFields As String, myValues As String, strSQL As String
With ActiveDocument
bSv = .Saved
For Each CCtrl In .ContentControls
With CCtrl
If .ShowingPlaceholderText = False Then
Select Case .Type
Case wdContentControlDate
DtFmt = .DateDisplayFormat
.DateDisplayFormat = "YYYY-MM-DD"
myFields = myFields & .Tag & ", "
myValues = myValues & "'" & .Range.Text & "', "
.DateDisplayFormat = DtFmt
Case wdContentControlRichText, wdContentControlText, wdContentControlDropdownList, wdContentControlComboBox
myFields = myFields & .Tag & ", "
myValues = myValues & "'" & .Range.Text & "', "
Case Else
End Select
End If
End With
Next
.Saved = bSv
End With
If myFields <> "" Then
myFields = Left(myFields, Len(myFields) - 2)
myValues = Left(myValues, Len(myValues) - 2)
strSQL = "INSERT INTO TABLE_1 (" & myFields & ") VALUES (" & myValues & ")"
Dim Conn As New ADODB.Connection
With Conn
.Open "DSN=ABCD": .Execute strSQL: .Close
End With
Set Conn = Nothing
MsgBox "Form data saved to database", vbInformation
Else
MsgBox "No form data found", vbExclamation
End If
End Sub
As you noticed, Word does not have Application.FindFormat or Application.ReplaceFormat, but if you know the format is m/d/y you should be able to do this:
myValues = myValues & "'" & ymd(control.Range.Text) & "'"
Function ymd(s as String) As String
Dim v As Variant
v = VBA.split(s, "/")
ymd = Right("0000" & v(2),4) & "-" & Right("00" & v(0),2) & "-" & Right("00" & v(1),2)
End Function
Everything else (e.g. the way you add commas to the list of dates) looks fine but I have not tested.

Import CSV and force all fields to Text format

I am importing a series of CSV files into Access tables. I have the following line that imports each file:
DoCmd.TransferText acImportDelim, , FN, F.Path, True
This import statement works and creates the necessary table. However, it creates the field types from the data, and depending on the first few rows of the data it may create a field as numeric that should be text - and then causes an error when it encounters a text value later in the file.
How can I force the field type to Text for every field in the input file? I've used Import Specifications before, but first the file format (provided by others outside my control) may change from time to time, and second it's a very "wide" file with 200+ column, so this isn't a practical answer.
This is not a great workaround, but I had to go through the process anyway to get around the 255 field limit in tables. In short, the import steps I ended up with are
Read the 1st line of the file as an inputstream
Split the line to get the field names, put them in a data dictionary table and then manually mark the ones I want to import
Use CREATE TABLE to create a new data table (selected fields only) with all of the fields set to TEXT
Read each line of the file as an inputstream
Split the line to get the data for each field
Use INSERT INTO to add the selected fields to the data table
Cumbersome, but it solves both problems - I'm not limited to 255 fields in the input files and I can control the data type of the fields as I create them.
The code, if anyone cares, is
Function Layout()
Set db = CurrentDb()
Folder = DLookup("[data folder]", "folder")
Dim FSO As New FileSystemObject
Set flist = FSO.GetFolder(Folder).Files
db.Execute ("delete * from [data dictionary]")
For Each F In flist
FN = Left(F.Name, InStr(F.Name, ".") - 1)
FT = Mid(F.Name, InStr(F.Name, ".") + 1)
If FT <> "csv" Then GoTo Skip
If TestFile(F.path) = "ASCII" Then
Set instream = FSO.OpenTextFile(F.path, ForReading, , 0)
Else: Set instream = FSO.OpenTextFile(F.path, ForReading, , -1)
End If
header = instream.ReadLine
Data = Split(header, ",")
For i = LBound(Data) To UBound(Data)
SQL = "insert into [data dictionary] ([table], [field], [index]) select "
SQL = SQL & "'" & FN & "','" & Data(i) & "','" & i & "'"
db.Execute SQL
Next i
Skip: Next F
End Function
Function TestFile(ByVal path As String)
Dim buffer As String
Dim InFileNum As Integer
Dim firstByte As Integer
Dim secondByte As Integer
Dim thirdByte As Integer
buffer = String(100, " ")
InFileNum = FreeFile
Open path For Binary Access Read As InFileNum
Get InFileNum, , buffer
Close InFileNum
firstByte = Asc(Mid(buffer, 1, 1))
secondByte = Asc(Mid(buffer, 2, 1))
thirdByte = Asc(Mid(buffer, 3, 1))
If (firstByte = 255 And secondByte = 254) Then
TestFile = "Unicode"
ElseIf (firstByte = 254 And secondByte = 255) Then
TestFile = "Unicode"
ElseIf (firstByte = 239 And secondByte = 187 And thirdByte = 191) Then
TestFile = "Unicode"
Else
TestFile = "ASCII"
End If
End Function
Function import()
Folder = DLookup("[data folder]", "folder")
Set db = CurrentDb()
Dim FSO As New FileSystemObject
Set Tlist = db.OpenRecordset("select [table] from [data dictionary] where ([required]<>'') group by [table]")
Tlist.MoveFirst
Do While Not Tlist.EOF
TN = Tlist.Fields("table").Value
Delete_table (TN)
Set flist = db.OpenRecordset("select * from [data dictionary] where [required]<>'' and [table]='" & TN & "'")
flist.MoveFirst
Text = ""
Do While Not flist.EOF
FN = flist.Fields("Field")
Text = Text & "," & FN & " " & IIf(InStr(FN, "Date") > 0 Or InStr(FN, "_DT") > 0, "DATETIME", "TEXT")
flist.MoveNext
Loop
SQL = "CREATE TABLE " & TN & "(" & Mid(Text, 2) & ")"
db.Execute SQL
path = Folder & "\" & TN & ".csv"
If TestFile(path) = "ASCII" Then
Set instream = FSO.OpenTextFile(path, ForReading, , 0)
Else: Set instream = FSO.OpenTextFile(path, ForReading, , -1)
End If
header = instream.ReadLine
Do While Not instream.AtEndOfStream
Line = parser(instream.ReadLine)
Data = Split(Line, ",")
flist.MoveFirst
Text = ""
Do While Not flist.EOF
n = flist.Fields("index").Value
Text = Text & ",'" & Data(n) & "'"
flist.MoveNext
Loop
SQL = "insert into [" & TN & "] values(" & Mid(Text, 2) & ")"
db.Execute SQL
Loop
Tlist.MoveNext
Loop
x = MultipleCodes()
MsgBox ("done")
End Function
Function parser(S)
parser = S
i = InStr(S, Chr(34))
If i = 0 Then
parser = S
Else
j = InStr(i + 1, S, Chr(34))
T = Mid(S, i + 1, j - i - 1)
T = Replace(T, ",", ";")
parser = Left(S, i - 1) & T & parser(Mid(S, j + 1))
End If
End Function

ADODB Recordset Open returns Error#:13

I've been using stackoverflow for over a year now but this is my first post so if I do something wrong, please let me know and I'll try to do better next time.
I'm currently using MS Access 2003 as a front-end data entry application with an MS SQL 2008 back end. A function used by just about every form in the app is breaking for no reason that I can determine when called from a specific subroutine.
Calling subroutine:
Private Sub Form_Load()
strRep = GetAppCtl("ConUID")
FLCnnStr = GetAppCtl("ConStrApp")
strSQL2 = "SELECT EMPNMBR, First, Last, TSLogin, IsITAdmin, " & _
" IsManager, Pwd, AppAuthLvl, SEX, AppTimeOutMins " & _
" FROM utEmplList WHERE EMPNMBR = " & _
strRep & ";"
Set cnn = New ADODB.Connection
With cnn
.ConnectionString = FLCnnStr
.Open
End With
Set rst = New ADODB.Recordset
rst.Open strSQL2, cnn, adOpenDynamic, adLockReadOnly
intAppAuthLvl = rst!AppAuthLvl
' Loaded/opened with parameters / arguments (OpenArgs)?
If Not IsNull(Me.OpenArgs) And Me.OpenArgs <> "" Then
Me.txtEmpSecLvl = Me.OpenArgs
Else
Me.txtEmpSecLvl = "99999<PROGRAMMER>Login:-1,-1\PWD/999|M!60$"
End If
Me.lblDateTime.Caption = Format(Now, "dddd, mmm d yyyy hh:mm AMPM")
If FirstTime <> "N" Then
' Set default SQL select statement with dummy WHERE clause
' (DealID will always be <> 0!)
strDate = DateAdd("d", -14, Now())
strSQLdefault1 = "SELECT *, DealHasTags([PHONE10],[REP]) as DealHasTags FROM utDealSheet WHERE DealID <> 0 AND (STATUS BETWEEN '00' AND '99') "
strSQLdefault2 = "SELECT *, DealHasTags([PHONE10],[REP]) as DealHasTags FROM utDealSheet WHERE DATE >= #" & strDate & "# AND DealID <> 0 AND (STATUS BETWEEN '00' AND '99') "
Me.LoggingDetail.Enabled = False
Me.LoggingDetail.Visible = False
If rst!AppAuthLvl <= 200 Then
strSQL = strSQLdefault1 & ";"
Me.LoggingDetail.Form.RecordSource = strSQL
Else
strSQL = strSQLdefault2 & ";"
Me.LoggingDetail.Form.RecordSource = strSQL
End If
FirstTime = "N"
End If
DoCmd.Maximize
End Sub
Function that is breaking:
Public Function GetAppCtl(strFldDta As String) As Variant
Dim strSQL As String
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim strConnString As String
If IsNull(strFldDta) Then GetAppCtl = "ERR"
' Starting string
strConnString = "ODBC;Description=SQLUmgAgr;DRIVER=SQL Server;SERVER="
' Set a connection object to the current Db (project)
Set cnn = CurrentProject.Connection
strSQL = "Select ConStrApp, ConStrTS, DftOfficeID, RecID, VerRelBld, SeqPrefix, ConDb, ConDbTs, ConUID, ConUIDTS, ConPWD, ConPWDTs, ConServer, ConServerTS, ConWSID, ConWSIDTS from tblAppCtl WHERE RecID = 1;"
Set rst = New ADODB.Recordset
rst.Open strSQL, cnn, adOpenKeyset, adLockReadOnly
' If a Db error, return 0
If Err.Number <> 0 Then
GetAppCtl = ""
GoTo CleanUp
End If
' If no record found, return 0
If rst.EOF Then
GetAppCtl = ""
Else ' Otherwise, return Version/Build
Select Case strFldDta
Case Is = "ConStrApp" ' connection string - application
strConnString = strConnString & Trim(rst!Conserver) & ";" _
& "UID=" & Trim(rst!ConUID) & ";PWD=" & Trim(rst!conpwd) & ";" _
& "DATABASE=" & Trim(rst!ConDb) & ";WSID=" & Trim(rst!ConWSID)
GetAppCtl = strConnString
Case Is = "ConStrTS" ' connection string - TouchStar
strConnString = strConnString & Trim(rst!ConserverTS) & ";" _
& "UID=" & Trim(rst!ConUIDTS) & ";PWD=" & Trim(rst!conpwdTS) & ";" _
& "DATABASE=" & Trim(rst!ConDbTS) & ";WSID=" & Trim(rst!ConWSID)
GetAppCtl = strConnString
Case Is = "DftOfficeID" ' Default AGR office ID
GetAppCtl = rst!DftOfficeID
Case Is = "VerRelBld" ' Current APP ver/rel/bld (to be checked against SQL Db
GetAppCtl = rst!VerRelBld
Case Is = "SeqPreFix" ' Sales seq# prefix (ID as per office for backward capability)
GetAppCtl = rst!SeqPrefix
Case Is = "ConUID"
GetAppCtl = rst!ConUID
End Select
End If
CleanUp:
rst.Close
Set rst = Nothing
cnn.Close
Set cnn = Nothing
End Function
The function is breaking here, but only when called by the above sub:
Set rst = New ADODB.Recordset
rst.Open strSQL, cnn, adOpenKeyset, adLockReadOnly
' If a Db error, return 0
If Err.Number <> 0 Then
GetAppCtl = ""
GoTo CleanUp
End If
When called from any other sub it works fine and returns the appropriate value. Please help.
I don't have an actual explanation as to why it was returning an error code but by removing the error checking the process worked. If anyone has an actual explanation as to what was actually causing the issue it would be greatly appreciated.
I know this post's a bit old and OP might have solved the problem.
I encountered the same problem and solved it by changing "Microsoft ActiveX Data Objects 2.5 Library" to "Microsoft ActiveX Data Objects 2.8 Library" from VBA Tools => References.

More efficinet way to filter form

I have the following code:
Public Function BuildSQL(stQueryName As String, stWhereClause As String) As String
On Error GoTo Err_BuildSQL
Dim SQLcmd As String
Dim intPos As Integer
Dim db As Database
Dim qryOrig As QueryDef
Set db = CurrentDb()
Set qryOrig = db.QueryDefs(stQueryName)
SQLcmd = qryOrig.SQL
intPos = InStr(SQLcmd, "WHERE")
If intPos > 0 Then
SQLcmd = Left(SQLcmd, intPos - 1)
End If
intPos = InStr(SQLcmd, ";")
If intPos > 0 Then
SQLcmd = Left(SQLcmd, intPos - 1)
End If
If Not (stWhereClause = "") Then
SQLcmd = Trim(SQLcmd) & " WHERE " & stWhereClause & ";"
Else
SQLcmd = Trim(SQLcmd) & ";"
End If
BuildSQL = SQLcmd
Exit_BuildSQL:
Set qryOrig = Nothing
Set db = Nothing
Exit Function
Err_BuildSQL:
MsgBox Err.Description
Resume Exit_BuildSQL
End Function
Private Sub SandBox_Click()
On Error GoTo Err_SandBox_Click
Dim db As Database
Dim rs As Recordset
Dim stSQL As String
Dim stFrmName As String
Dim stQryName As String
Dim stSQLWhere As String
Dim stIDList As String
stFrmName = "Libri"
stQryName = "Libri_All_Query"
'Define WHERE clause
stSQLWhere = ""
If Not (IsNull([Forms]![Libreria]![Editore]) Or [Forms]![Libreria]![Editore] = "") Then
stSQLWhere = stSQLWhere & "Libri_Editori.Editore = '" & [Forms]![Libreria]![Editore] & "'"
End If
If Not (IsNull([Forms]![Libreria]![CognomeAutore]) Or [Forms]![Libreria]![CognomeAutore] = "") Then
If (stSQLWhere = "") Then
stSQLWhere = stSQLWhere & "Autori.Cognome = '" & [Forms]![Libreria]![CognomeAutore] & "'"
Else
stSQLWhere = stSQLWhere & " AND Autori.Cognome = '" & [Forms]![Libreria]![CognomeAutore] & "'"
End If
End If
'Here several more fields of the search form will be checked and added
stSQL = BuildSQL(stQryName, stSQLWhere)
'*** Code in question!
Set db = CurrentDb()
Set rs = db.OpenRecordset(stSQL)
If Not (rs.EOF And rs.BOF) Then
stIDList = "("
rs.MoveFirst
Do Until rs.EOF = True
If (stIDList = "(") Then
stIDList = stIDList & rs.Fields(0)
Else
stIDList = stIDList & ", " & rs.Fields(0)
End If
rs.MoveNext
Loop
stIDList = stIDList & ")"
Else
Err.Description = "Errore! Recordset vuoto."
Resume Err_SandBox_Click
End If
DoCmd.OpenForm stFrmName, , , , acFormReadOnly
Access.Forms(stFrmName).RecordSource = "SELECT * FROM Libri WHERE Libri.ID IN " & stIDList
'**** End code in question
Exit_SandBox_Click:
Set db = Nothing
Set rs = Nothing
Exit Sub
Err_SandBox_Click:
MsgBox Err.Description
Resume Exit_SandBox_Click
End Sub
This code works as I want but "looks" slow even with a test DB with only a few records in each table.
I believe the time is spent (how can I check if this is true?) in the loop between comments.
Is there a more basic, obvious and efficient way to filter the form than creating a recordset and looping through it as I am doing?
The form "Libri" is a big one with several subform to be able to see all the data of a Book.
The query "Libri_All_Query" is a join of almost all tables in the DB and the code shown is executed from a form where I plan to add all possible search fields.
Forms have a filter property:
stWhereClause = "Title Like '" & Me.txtSearch & "*'"
Me.Filter = stWhereClause
Me.FilterOn = True
The filter should be constructed in a similar way to a WHERE statement. There are some limitations compared with Where. You may wish to check with DCount that records will be returned.
EDIT
If you want a set of records where a subform contains only certain records, you need something on these lines:
SELECT b.Title
FROM Books b
WHERE b.ID IN (
SELECT j.BookID FROM BooksAuthorJunction j
INNER JOIN Authors a ON j.AuthorID = a.ID
WHERE a.Author Like "Arn*")
There are advantages in building more that one form, books as a main form and authors as a subform, then authors as a main form and books as a subform. It is often easier on the user.

vb 6.0 can anyone help me with my code?

Im working with my project inventory system i want to display the filtered dates in my books table in the mysql in my listview1 using 2 DTPicker and make a report for it. Im having an error in my query in the classmodule idk if its only the query and im really confused im a begginer in vb 6.0...please in need your help guys.
Im using 2 tables namely books and supplier.
MY CODE IN THE 'CLASS MODULE':
Sub DisplayList(ListView1 As ListView, DateFrom As Date, DateTo As Date)
Dim lstItem As ListItem, a As Integer
Dim rs As New ADODB.Recordset
Dim sql As String
If rs.State = adStateOpen Then rs.Close
sql = " SELECT supplier.category,books.title,books.dataAcquired,books.amount,books.quantity,books.accesionno,books.conditions" & _
" From supplier INNER JOIN books" & _
" ON supplier.code=books.code" & _
" WHERE (((books.dataAcquired)>=#" & DateFrom & "#) and ((books.dataAcquired) <=#" & DateTo & "#))" & _
" GROUP BY supplier.category,books.title,books.dataAcquired,books.amount,books.quantity,books.accesionno,books.conditions" & _
" ORDER BY books.dataAcquired DESC;"
rs.Open sql, cnn
ListView1.ListItems.Clear
Do While Not rs.EOF
a = a + 1
Set lstItem = ListView1.ListItems.Add(, , a, 1, 1)
lstItem.SubItems(1) = rs(0).Value
lstItem.SubItems(2) = rs(1).Value
lstItem.SubItems(3) = rs(2).Value
lstItem.SubItems(4) = rs(3).Value
lstItem.SubItems(5) = rs(4).Value
lstItem.SubItems(6) = rs(5).Value
lstItem.SubItems(7) = rs(6).Value
rs.MoveNext
Loop
End Sub
MY CODE IN MY FORM:
Private Sub Show_Click()
clsData.DisplayList ListView1, DTPicker1.Value, DTPicker2.Value
lblCount.Caption = ListView1.ListItems.Count
End Sub
Private Sub Form_Load()
DTPicker1.Value = Date
DTPicker2.Value = Date
End Sub
Private Sub Form_Activate()
clsData.DisplayList ListView1, DTPicker1.Value, DTPicker2.Value
lblCount.Caption = ListView1.ListItems.Count
End Sub
Change # by '
format date how yyyy-MM-dd or yyyyMMdd
sql = " SELECT supplier.category,books.title,books.dataAcquired,books.amount,books.quantity,books.accesionno,books.conditions" & _
" From supplier INNER JOIN books" & _
" ON supplier.code=books.code" & _
" WHERE (((books.dataAcquired)>='" & format(DateFrom,"yyyy-MM-dd") & "') and ((books.dataAcquired) <='" & format(DateTo,"yyyy-MM-dd") & "'))" & _
" GROUP BY supplier.category,books.title,books.dataAcquired,books.amount,books.quantity,books.accesionno,books.conditions" & _
" ORDER BY books.dataAcquired DESC;"
change loop while added validations for recordset emptys, some how
if RecordsetIsClosed(rs) then exit sub
While Not RecordSetIsEmpty(rs)
a = a + 1
Set lstItem = ListView1.ListItems.Add(, , a, 1, 1)
lstItem.SubItems(1) = rs(0).Value
lstItem.SubItems(2) = rs(1).Value
lstItem.SubItems(3) = rs(2).Value
lstItem.SubItems(4) = rs(3).Value
lstItem.SubItems(5) = rs(4).Value
lstItem.SubItems(6) = rs(5).Value
lstItem.SubItems(7) = rs(6).Value
rs.MoveNext
wend
Public Function RecordSetIsEmpty(ByRef rs As ADODB.Recordset) As Boolean
' On Local Error GoTo RecordSetIsEmpty_Error
' RecordSetIsEmpty = True
' If rs Is Nothing Then
' RecordSetIsEmpty = True
' Exit Function
' End If
' If RecordsetIsClosed(rs) = True Then
' RecordSetIsEmpty = True
' Exit Function
' End If
RecordSetIsEmpty = (rs.BOF = True And rs.EOF = True)
' RecordSetIsEmpty_Done:
' Exit Function
' RecordSetIsEmpty_Error:
' Resume RecordSetIsEmpty_Done
End Function
Public Function RecordsetIsClosed(ByRef rs As ADODB.Recordset) As Boolean
On Local Error GoTo RecordsetIsClosed_Error
RecordsetIsClosed = True
If rs Is Nothing Then
RecordsetIsClosed = True
End If
If rs.State <> adStateClosed Then
RecordsetIsClosed = False
End If
RecordsetIsClosed_Done:
Exit Function
RecordsetIsClosed_Error:
Resume RecordsetIsClosed_Done
End Function
Dont forget to open the database connection
updated thanks Mark Bertenshaw
RecordSetIsEmpty is use for problems when do movenext.. well i remember
RecordsetIsClosed is use because in some cases and databases managers return not recordset or the recordset is not correct initialized
for example access is necessary use movefist before do movenext or read values