Un-nesting nested MS Access sql queries - ms-access

I've been given a small Access database to work on. The guy who created it wrote many custom queries to generate reports. I've been tasked with modifying the reports and the guy who initially wrote the queries is gone and left no documentation.
My biggest issue is that he nested the queries 5+ levels deep and it's incredibly difficult for me to read the way it is. The queries he wrote generally have this format but are way more complex.
SELECT thisCol, thatCol, theOtherCol
FROM CustomQuery1, CustomQuery2, CustomQuery3
And CustomQuery{1,2,3} are each written the exact same way where they reference multiple other sub-queries. Not only do I find this incredibly hard to read but I worry if I modify one of the queries that perhaps it's called elsewhere in another query that I'm not aware of that will break another report. I'm wondering if there's a way to analyze all the queries to figure out which ones are called by what other queries and/or if there's some tool out there that could automatically un-nest them or if I just have to trace through them all manually.

One thing that will help you a good deal is the Object Dependencies pane, which is built into Access. Note that you'll need to turn on Name AutoCorrect while checking that, even if you have it off the rest of the time, as is usually best. Also, it won't display VBA code references to queries, so you'll have to check those yourself by searching.

Yes, there's a way to determine if a query is used by another query. I created a form to do exactly this so that I could select multiple database objects and delete them simultaneously because I hated how Access only lets you natively delete 1 database object (i.e. form, table, etc...) at a time, and I wanted to make sure that the database objects I wanted to delete weren't referenced elsewhere.
Unfortunately, I can't upload the form anywhere from my work computer, they block that stuff. However, I can tell you that what you have to do is search through the QueryDef.SQL of each QueryDef in your database.
You're going to have to pick this apart a little, but this is the VBA I wrote to do it.
Private Sub ListObjects_Click()
' Search all queries for SQL containing the specified string.
Screen.MousePointer = 11
On Error GoTo Err_SearchQueries
Dim db As DAO.Database
Dim qdf As QueryDef
Dim varTest As Variant
Dim lngSearchCount As Long
Dim lngFoundCount As Long
Set db = CurrentDb
lngFoundCount = 0
lngSearchCount = 0
Me.txtTblSearch = "*** Beginning search for " & Me.ListObjects.Column(0) & "..." & vbCrLf
'Get a count of the database objects that will be searched
For Each qdf In db.QueryDefs
With qdf
If Left(qdf.Name, 3) = "~sq" Then
lngSearchCount = lngSearchCount + 1
End If
End With
Next qdf
For Each qd In db.QueryDefs
If InStr(1, qd.SQL, Me.ListObjects.Column(0)) > 0 Then
If Left(qd.Name, 4) = "~sq_" Then
If Mid(qd.Name, 5, 1) = "f" Then
Me.txtTblSearch = txtTblSearch & "found in Form " & Right(qd.Name, Len(qd.Name) - 5) & vbCrLf
lngFoundCount = lngFoundCount + 1
ElseIf Mid(qd.Name, 5, 1) = "r" Then
Me.txtTblSearch = txtTblSearch & "found in Report " & Right(qd.Name, Len(qd.Name) - 5) & vbCrLf
lngFoundCount = lngFoundCount + 1
ElseIf Mid(qd.Name, 5, 1) = "d" Then
Me.txtTblSearch = txtTblSearch & "found in Report " & Right(qd.Name, Len(qd.Name) - 5) & vbCrLf
lngFoundCount = lngFoundCount + 1
ElseIf Mid(qd.Name, 5, 1) = "c" Then
Me.txtTblSearch = txtTblSearch & "found in a control in Form " & Right(qd.Name, Len(qd.Name) - 5) & vbCrLf
lngFoundCount = lngFoundCount + 1
End If
Else
Me.txtTblSearch = txtTblSearch & "found in Query " & qd.Name & vbCrLf
lngFoundCount = lngFoundCount + 1
End If
End If
Next qd
Set qd = Nothing
Set db = Nothing
Exit_SearchQueries:
Set qdf = Nothing
Set db = Nothing
Me.txtTblSearch = Me.txtTblSearch & vbCrLf
Me.txtTblSearch = Me.txtTblSearch & "*** Searched " & lngSearchCount & _
" objects, found " & lngFoundCount & " occurrences."
Screen.MousePointer = 0
Exit Sub
'If an error is thrown, alert the user as to which object caused it
Err_SearchQueries:
MsgBox Err.Description, vbExclamation, "Error " & Err.Number
If IsNull(qd.Name) Then
Else
MsgBox "Possible issue with query: " & qd.Name
End If
Screen.MousePointer = 0
Resume Exit_SearchQueries
End Sub
Here's a pic of the form in action, to give you an idea:

Related

VBA SQL (MariaDB) - Query SELECT * duplicates first row, and miss last one

I have an issue while trying to get every line from a table in my database. In VBA when requesting the table with 'SELECT * FROM companies;', the results duplicates the first row, and remove the last one. As a result, I have 3 records, which corresponds to the real number of records in my DB, but instead of having 1, 2 and 3, I have 1, 1 and 2.
Any idea?
You can see here Database records for table 'companies', when requesting 'SELECT * FROM companies': DB Records
You can see here the result of the same request in Excel/VBA using the following code:
sqlQuery = "SELECT ALL * FROM companies;"
rsDB.Open sqlQuery, conDB, adOpenDynamic
Do While Not rsDB.EOF
For Each col In rsDB.GetRows
Debug.Print col
Next
Loop
Results: VBA request
Would love to get any piece of advice on this issue!
The fun fact is that if I try to select only one column of the table, such as 'idCompany', then I have the result '1, 2, 3' with VBA, which is fine. The real issue only appears when using '*'.
Thanks a lot for your time,
--- EDIT
The connection string used to connect to the DB:
Set conDB = New ADODB.Connection
Set rsDB = New ADODB.recordSet
Set rsDBCol = New ADODB.recordSet
conDB.connectionString = "DRIVER={MariaDB ODBC 3.1 Driver};" _
& "SERVER=s-mypricing-1;" _
& "DATABASE=db_pricing;" _
& "PORT=3306" _
& "UID=user;" _
& "PWD=pwd;" _
& "OPTION=3"
conDB.Open
rsDB.CursorLocation = adUseServer
rsDBCol.CursorLocation = adUseServer
Difficult to test, but I suspect you need this instead:
rsDB.MoveFirst
Do While Not rsDB.EOF
For Each fld In rsDB.Fields
Debug.Print fld.Name & ": " & fld.Value
Next
rsDB.MoveNext
Loop
When you iterate an ADO recordset, the object itself represents a current row. So you refer to the Fields of the current row to get the columns. And the properties of each field to get descriptive information about that cell (name of column, value in cell).
Through the comments we learned that the issue is related to opening the recordset with adOpenDynamic mode. What follows is code that should represent a working case for MaraiaDB.
Set conDB = New ADODB.Connection
Set rsDB = New ADODB.recordSet
Set rsDBCol = New ADODB.recordSet
conDB.connectionString = "DRIVER={MariaDB ODBC 3.1 Driver};" _
& "SERVER=s-mypricing-1;" _
& "DATABASE=db_pricing;" _
& "PORT=3306" _
& "UID=user;" _
& "PWD=pwd;" _
& "OPTION=3"
conDB.Open
rsDB.CursorLocation = adUseServer
rsDBCol.CursorLocation = adUseServer
sqlQuery = "SELECT ALL * FROM companies;"
With rsDB.Open(sqlQuery, conDB)
If Not (.BOF And .EOF) Then
.MoveFirst
Do Until .EOF
For Each fld In .Fields
Debug.Print fld.Name & ": " & fld.Value
Next
.MoveNext
Loop
End If
.Close
End With
conDB.Close

Access 2013 VBA - new records from INSERT INTO not found by subsequent SELECT query

I am updating an Access 2013 table through VBA. My task requires certain records to be added to the table during a loop and subsequently records to be read from the (updated) table. I am finding that my code works as expected provided I run through the code line by line in debug mode. However, if I run the code using F5, my results will be unpredictable. Sometimes the code works as expected and other times the loop finishes early. It looks as though the newly added records are not found by a select query, even though they have been added to the table. Referring to the code below, the INSERT INTO statement at the bottom is executed, but the subsequent opening of the adrsb recordset sometimes does not find the updated records, causing the loop to terminate early. I've been stumped on this for days now despite my best efforts in debugging. Any help will be very gratefully received. :)
Do
i = i + 1
'Debug.Assert i <> 4
If adrsb.State = 1 Then
adrsb.Close
Set adrsb = Nothing
Set adrsb = New ADODB.Recordset
adrsb.ActiveConnection = CurrentProject.Connection
adrsb.CursorType = adOpenStatic
End If
'adrsb.CursorType = adOpenDynamic
adrsb.Open "SELECT tblInScopeRestructures.Code1, tblInScopeRestructures.Gen " & _
"FROM tblInScopeRestructures " & _
"GROUP BY tblInScopeRestructures.Code1, tblInScopeRestructures.Gen " & _
"HAVING (((tblInScopeRestructures.Gen)=" & i & "))" & _
"ORDER BY tblInScopeRestructures.Code1;"
adrsb.Requery
Dim adrsc As ADODB.Recordset
Set adrsc = New ADODB.Recordset
adrsc.ActiveConnection = CurrentProject.Connection
adrsc.CursorType = adOpenStatic
If Not adrsb.EOF Then
adrsb.MoveLast
adrsb.MoveFirst
End If
If adrsb.RecordCount <> 0 Then
adrsb.MoveFirst
'strPrevCode1 = adrsb.Fields("Code1")
Do While Not adrsb.EOF
strPrevCode1 = adrsb.Fields("Code1")
If adrsc.State = 1 Then
adrsc.Close
End If
adrsc.CursorType = adOpenStatic
adrsc.Open "SELECT tblRestructure.Code1, tblRestructure.Code2, tblRestructure.RecDate " & _
"FROM tblRestructure " & _
"WHERE (((tblRestructure.Code2)='" & strPrevCode1 & "'));"
If adrsc.RecordCount <> 0 Then
adrsc.MoveFirst
Do While Not adrsc.EOF
adConn.Execute ("INSERT INTO tblInScopeRestructures(Code1,Code2,RecDate,Gen) VALUES ('" & adrsc.Fields("Code1") & "','" & adrsc.Fields("Code2") & _
"',#" & Format(adrsc.Fields("RecDate"), "mm/dd/yyyy") & "#," & i + 1 & ")")
Debug.Print adrsc.Fields("Code1") & adrsc.Fields("Code2")
Debug.Print i + 1
For j = 1 To 100000
Next j
adrsc.MoveNext
Loop
End If
adrsb.MoveNext
If adrsc.State = 1 Then
adrsc.Close
End If
Loop
End If
Debug.Assert adrsb.RecordCount <> 0
Loop While adrsb.RecordCount <> 0
I reckon the problem is probably here
adrsb.CursorType = adOpenStatic
change it to
adrsb.CursorType = adOpenDynamic
And Instead of this bit of code:
For j = 1 To 100000
Next j
You could try something slightly less thrashy such as:
DoEvents
And maybe after the DoEvents command, you could try adding a Requery command on your ADODB recordset.
Except you will probably lose your desired cursor position, so before doing the Refresh, you can record the ID of your primary key in a variable and then find that cursor location in the recordset
intID = adrsb.Fields("MyKey")
adrsb.Requery
rs.Find "MyKey = " & intID
Ok, I've got a solution of sorts. I inserted the following code to cause a pause immediately after the second EXECUTE INTO operation:
TWait = Time
TWait = DateAdd("s", 5, TWait)
Do Until TNow >= TWait
TNow = Time
Loop
This slows the code down very significantly, but it works. I experimented with shorter pauses but tended to get the same problems with the loop sometime exiting early. While the immediate problem is solved, I'm left a bit stunned that this is necessary and am worried about when such an issue will next raise its head.

Should I re-purpose subform controls on one form or just create multiple forms?

In my office of 65 people, I want to create a "portal" for all the employees out of a single .accdb file. It will allow each employee to navigate to a new "screen" from a dropdown menu.
Should I use a single form with plug-and-play subform controls in order to centralize the VBA code, or should I just use different forms?
I'm thinking it would be nice to have one form with plug-and-play subform controls. When the employee selects a new "screen", the VBA just sets the SourceObject property of each subform control and then re-arranges the subforms based on the layout of the selected "screen".
For instance, we currently use a couple of Access database forms to enter and review errors that we find in our workflow system. So in this scenario, to review the errors I would just say
SubForm1.SourceObject = "Form.ErrorCriteria"
SubForm2.SourceObject = "Form.ErrorResults"
And then I would just move them into place (these values would be pulled dynamically based upon the "screen" selected):
SubForm1.Move WindowWidth * 0.05, WindowHeight * 0.05, WindowWidth * 0.9, WindowHeight * 0.2
SubForm2.Move WindowWidth * 0.05, WindowHeight * 0.25, WindowWidth * 0.9, WindowHeight * 0.65
So this creates a small header section (SubForm1) on the form where I can select the criteria for the errors I want to see (data range, which team committed the error, etc) and then I can view the errors in the much larger section below the header (SubForm2) that holds the datasheet with the results.
I can propogate events up to the main form from the ErrorCriteria and ErrorResults forms that are now bound to the subform controls. That will help me to use the basic MVC design pattern for VBA described here. I can treat the main form as the view, even though parts of that view are buried in subform controls. The controller only has to know about that one view.
My problem comes when the user selects a new "screen" from the dropdown menu. I think it would be nice to just re-purpose the subform controls, like so:
SubForm1.SourceObject = "Form.WarehouseCriteria"
SubForm2.SourceObject = "Form.InventoryResults"
And then just move/resize those subforms to the appropriate layout for the "Inventory" screen.
This approach seems to make the user interface design cleaner in my mind because you basically only ever have to deal with one main form that acts as a template and then you plug in the values (the SourceObject properties) into that template.
But each time we change the "screen", we have a totally different "Model" behind the scenes and a new "View" too according to the MVC design pattern. I wonder if that would clutter up the MVC VBA code behind the scenes, or if the VBA code itself could be modularized too (possibly using Interfaces) to make it just as adaptable as the user interface.
What is the cleanest way to do this from both a User Interface perspective, and from a VBA perspective. Use one main form as template where other forms could be swapped in and out as subforms, or just close the current form and open a new form when the user selects a new "screen" from the dropdown menu.
Below is a brief description of one way to 'repurpose' or reformat a form for several uses. Re your question of changing the VBA code, a simple solution would be to check a label value or some value you set in the control, then call the appropriate VBA subroutine.
We had over 100 reports available, each with their own selection criteria/options and we did not want to create a unique filter form for every report. The solution was to identify the selection options available by report, identify the logical order of those options, then create a table that would present the options to the user.
First, we created the table: ctlReportOptions (PK = ID, ReportName, OptionOrder)
Fields: ID (Int), ReportName (text), OptionOrder (Int), ControlName (text), ControlTop (Int), ControlLeft (Int), SkipLabel (Y/N), ControlRecordsourc(text)
Note 1: ID is not an AutoNumber.
Next we populated with records that would define the view the user would see.
Note 2: Using an ID of zero, we created records for EVERY field on the report so we could always redraw for the developers.
Then we created the form and placed controls for every possible filter.
We set the 'Default Value' property to be used as our default.
Some of the controls:
ComboBox to select the report name. Add code for Change event as follows:
Private Sub cboChooseReport_Change()
Dim strSQL As String
Dim rs As ADODB.recordSet
Dim i As Integer
Dim iTop As Integer
Dim iLeft As Integer
Dim iLblTop As Integer
Dim iLblLeft As Integer
Dim iLblWidth As Integer
Dim iTab As Integer
Dim strLabel As String
On Error GoTo Error_Trap
' Select only optional controls (ID <> 0); skip cotrols always present.
strSQL = "SELECT ctlRptOpt.ControlName, 'lbl' & Mid([ControlName],4,99) AS LabelName, SkipLabel " & _
"From ctlRptOpt WHERE (((ctlRptOpt.ID)<>0)) " & _
"GROUP BY ctlRptOpt.ControlName, 'lbl' & Mid([ControlName],4,99), SkipLabel;"
Set rs = New ADODB.recordSet
rs.Open strSQL, CurrentProject.Connection, adOpenDynamic
Do While Not rs.EOF
Me(rs!ControlName).Visible = False ' Hide control
If rs!skiplabel = False Then ' Hide Label if necessary
Me(rs!LabelName).Visible = False
End If
rs.MoveNext
Loop
rs.Close
iTop = 0
iTab = 0
' Get list of controls used by this report; order by desired sequence.
strSQL = "select * from ctlRptOpt " & _
"where [ID] = " & Me.cboChooseReport.Column(3) & _
" order by OptionOrder;"
Set rs = New ADODB.recordSet
rs.Open strSQL, CurrentProject.Connection, adOpenDynamic
If rs.EOF Then ' No options needed
Me.cmdShowQuery.Visible = True
Me.lblReportCriteria.Visible = False
Me.cmdShowQuery.left = 2000
Me.cmdShowQuery.top = 1500
Me.cmdShowQuery.TabIndex = 1
Me.cmdReset.Visible = False
rs.Close
Set rs = Nothing
GoTo Proc_Exit ' Exit
End If
' Setup the display of controls.
Me.lblReportCriteria.Visible = True
Do While Not rs.EOF
If rs!skiplabel = False Then
strLabel = "lbl" & Mid(rs!ControlName, 4)
iLblWidth = Me.Controls(strLabel).Width
Me(strLabel).top = rs!ControlTop
Me(strLabel).left = rs!ControlLeft - (Me(strLabel).Width + 50)
Me(strLabel).Visible = True
End If
iTab = iTab + 1 ' Set new Tab Order for the controls
Me(rs!ControlName).top = rs!ControlTop
Me(rs!ControlName).left = rs!ControlLeft
Me(rs!ControlName).Visible = True
If left(rs!ControlName, 3) <> "lbl" Then
Me(rs!ControlName).TabIndex = iTab
End If
If Me(rs!ControlName).top >= iTop Then
iTop = rs!ControlTop + Me(rs!ControlName).Height ' Save last one
End If
' If not a label and not a 'cmd', it's a filter! Set a default.
If left(rs!ControlName, 3) <> "lbl" And left(rs!ControlName, 3) <> "cmd" Then
If Me(rs!ControlName).DefaultValue = "=""*""" Then
' Me(rs!ControlName) = "*"
ElseIf left(Me(rs!ControlName).DefaultValue, 2) = "=#" And right(Me(rs!ControlName).DefaultValue, 1) = "#" Then
i = Len(Me(rs!ControlName).DefaultValue)
' Me(rs!ControlName) = Mid(Me(rs!ControlName).DefaultValue, 3, i - 3)
ElseIf Me(rs!ControlName).DefaultValue = "True" Then
' Me(rs!ControlName) = True
ElseIf Me(rs!ControlName).DefaultValue = "False" Then
' Me(rs!ControlName) = False
End If
Else
If Me(rs!ControlName).top + Me(rs!ControlName).Height >= iTop Then
iTop = rs!ControlTop + Me(rs!ControlName).Height ' Save last one
End If
End If
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
If Me.cboChooseReport.Column(1) <> "rptInventoryByDate" Then ' It's special
Me.cmdShowQuery.Visible = True
Me.cmdShowQuery.left = 2000
Me.cmdShowQuery.top = iTop + 300
iTab = iTab + 1
Me.cmdShowQuery.TabIndex = iTab
Else
Me.cmdShowQuery.Visible = False
End If
Me.cmdReset.Visible = True
Me.cmdReset.left = 5000
Me.cmdReset.top = iTop + 300
Me.cmdReset.TabIndex = iTab + 1
Proc_Exit:
Exit Sub
Error_Trap:
Err.Source = "Form_frmReportChooser: cboChooseReport_Change at Line: " & Erl
DocAndShowError ' Save error to database for analysis, then display to user.
Resume Proc_Exit ' Exit code.
Resume Next ' All resumption if debugging.
Resume
End Sub
lblReportCriteria: We displayed the final set of filters so when users complained of nothing showing on the report, we asked them to send us a screen print. We also passed this text to the report and it was printed as a footer on the last page.
cmdReset: Reset all controls back to their default values.
cmdShowQuery: Executes the running of the report
Private Sub cmdShowQuery_Click()
Dim qdfDelReport101 As ADODB.Command
Dim qdfAppReport101 As ADODB.Command
Dim qdfDelReport102 As ADODB.Command
Dim qdfAppReport102 As ADODB.Command
Dim qryBase As ADODB.Command
Dim strQueryName As String
Dim strAny_Open_Reports As String
Dim strOpen_Report As String
Dim qdfVendorsInfo As ADODB.Command
Dim rsVendorName As ADODB.recordSet
Dim strVendorName As String
Dim rsrpqFormVendorsInfo As ADODB.recordSet
On Error GoTo Error_Trap
If Not IsNull(Me.cboChooseReport.value) And Me.cboChooseReport.value <> " " Then
strAny_Open_Reports = Any_Open_Reports()
If Len(strAny_Open_Reports) = 0 Then
If Me.cboChooseReport.value = "rptAAA" Then
BuildReportCriteria '
If Me.chkBankBal = True Then
DoCmd.OpenReport "rptAAA_Opt1", acViewPreview
Else
DoCmd.OpenReport "rptAAA_Opt2", acViewPreview
End If
ElseIf Me.cboChooseReport.value = "rptBBB" Then
If IsNull(Me.txtFromDate) Or Not IsDate(Me.txtFromDate) Then
MsgBox "You must enter a valid From Date", vbOKOnly, "Invalid Date"
Exit Sub
End If
If IsNull(Me.txtToDate) Or Not IsDate(Me.txtToDate) Then
MsgBox "You must enter a valid To Date", vbOKOnly, "Invalid Date"
Exit Sub
End If
Me.txtStartDate = Me.txtFromDate
Me.txtEndDate = Me.txtToDate
DoCmd.OpenReport Me.cboChooseReport.value, acViewPreview
ElseIf Me.cboChooseReport.value = "rptCCC" Then
If Me.txtVendorName = "*" Then
gvstr_VendorName = "*"
Else
Set rsVendorName = New ADODB.recordSet
rsVendorName.Open "selVendorName", gv_DBS_Local, adOpenDynamic
Set qdfVendorsInfo = New ADODB.Command
qdfVendorsInfo.ActiveConnection = gv_DBS_SQLServer
qdfVendorsInfo.CommandText = ("qryVendorsInfo")
qdfVendorsInfo.CommandType = adCmdStoredProc
strVendorName = rsVendorName("VendorName")
gvstr_VendorName = strVendorName
End If
DoCmd.OpenReport "rptFormVendorReport", acViewPreview
Else
BuildReportCriteria
If Me.cboChooseReport.value = "rptXXXXXX" Then
ElseIf Me.cboChooseReport.value = "rptyyyy" Then
On Error Resume Next ' All resumption if debugging.
DoCmd.DeleteObject acTable, "temp_xxxx"
On Error GoTo Error_Trap
Set qryBase = New ADODB.Command
qryBase.ActiveConnection = gv_DBS_Local
qryBase.CommandText = ("mtseldata...")
qryBase.CommandType = adCmdStoredProc
qryBase.Execute
End If
DoCmd.Hourglass False
DoCmd.OpenReport Me.cboChooseReport.value, acViewPreview
End If
Else
MsgBox "You cannot open this form/report because you already have a form/report(s) open: " & _
vbCrLf & strAny_Open_Reports & _
vbCrLf & "Please close the open form/report(s) before continuing."
strOpen_Report = Open_Report
DoCmd.SelectObject acReport, strOpen_Report
DoCmd.ShowToolbar "tbForPost"
End If
Else
MsgBox "Please Choose Report", vbExclamation, "Choose Report"
End If
Exit Sub
Error_Trap:
Err.Source = "Form_frmReportChooser: cmdShowQuery_Click - Report: " & Nz(Me.cboChooseReport.value) & " at Line: " & Erl
If Err.Number = 2501 Then ' MsgBox "You chose not to open this report.", vbOKOnly, "Report cancelled"
Exit Sub
ElseIf Err.Number = 0 Or Err.Number = 7874 Then
Resume Next ' All resumption if debugging.
ElseIf Err.Number = 3146 Then ' ODBC -- call failed -- can have multiple errors
Dim errLoop As Error
Dim strError As String
Dim Errs1 As Errors
' Enumerate Errors collection and display properties of each Error object.
i = 1
Set Errs1 = gv_DBS_SQLServer.Errors
Err.Description = Err.Description & "; Err.Count = " & gv_DBS_SQLServer.Errors.Count & "; "
For Each errLoop In Errs1
With errLoop
Err.Description = Err.Description & "Error #" & i & ":" & " ADO Error#" & .Number & _
" Description= " & .Description
i = i + 1
End With
Next
End If
DocAndShowError ' Save error to database for analysis, then display to user.
Exit Sub
Resume Next ' All resumption if debugging.
Resume
End Sub
Function to build a string showing all of the selection criteria:
Function BuildReportCriteria()
Dim frmMe As Form
Dim ctlEach As Control
Dim strCriteria As String
Dim prp As Property
Dim strSQL As String
Dim rs As ADODB.recordSet
On Error GoTo Error_Trap
strSQL = "select * from ctlRptOpt " & _
"where ID = " & Me.cboChooseReport.Column(3) & _
" order by OptionOrder;"
Set rs = New ADODB.recordSet
rs.Open strSQL, CurrentProject.Connection, adOpenDynamic
If rs.EOF Then
strCriteria = " Report Criteria: None"
Else
strCriteria = " Report Criteria: "
End If
Do While Not rs.EOF
Set ctlEach = Me.Controls(rs!ControlName)
If ctlEach.ControlType = acTextBox Or ctlEach.ControlType = acComboBox Then
If ctlEach.value <> "*" And ctlEach.Name <> "cboChooseReport" And ctlEach.Name <> "cboLocCountry" Then
strCriteria = strCriteria & ctlEach.Tag & " = " & ctlEach.value & " , "
End If
End If
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
If Me.chkOblBal = -1 Then
strCriteria = strCriteria & "Non-zero balances only = Yes"
Else
'return string with all choosen criteria and remove last " , " from the end of string
strCriteria = left$(strCriteria, Len(strCriteria) - 3)
End If
fvstr_ReportCriteria = strCriteria
Set ctlEach = Nothing
Exit Function
Error_Trap:
If Err.Number = 2447 Then
Resume Next ' All resumption if debugging.
End If
Err.Source = "Form_frmReportChooser: BuildReportCriteria at Line: " & Erl
DocAndShowError ' Save error to database for analysis, then display to user.
Exit Function
Resume Next ' All resumption if debugging.
End Function
Finally, each report had it's own query that would filter based on the values in the controls on this form.
Hope this helps. If you are curious about any of the weird things you see, let me know. (i.e. we always used line numbers in the code (I deleted before posting) that allowed us to identify exact line where code fails)

Access - Missing query code in select query

I miss some query in my database. It lost before two days and select query stays without code only select; rest there. It hit several select query in my databases. Simply when I open sql interface in Access there miss query code.
I restarted it several time but code is missing, its look really crazy from my point of view but haven't you experiences with it?
Yes, it's happened to me.
I think it's an office bug. I noticed that happens when trying to use many different nested query.
So, unfortunately, the only way to restore lost queries is... from a backup.
Try to semplificate the structure of your nested queries.
I backup all my MS ACCESS queries with a script I found, Saved me MANY times.
Dim db As Object
Dim qdf As Object
Dim ff As Long
Backup_File = gDBPATH() & "\Bin\Backup_of_All_DB_Queries-" & MonthName(Month(Now()), False) & "-" & Day(Now()) & "-" & Year(Now()) & ".txt"
Backup_File_Msg = "Backup all DB queries to: " & Backup_File & " ?"
MyMsg = MsgBox(Backup_File_Msg, 260, "Query Backup")
If MyMsg = 6 Then
Set db = CurrentDb
ff = FreeFile()
Open Backup_File For Output As #ff
For Each qdf In db.QueryDefs
Print #ff, "Query: " & qdf.Name & vbCrLf
Print #ff, "SQL: " & qdf.SQL
Print #ff, "-----------------------------------------------" & vbCrLf
Next qdf
Close #ff
Backup_File_Msg = "Completed backup of all DB queries to: " & Backup_File
MyMsg = MsgBox(Backup_File_Msg, vbOKOnly, "Query Backup")
End If

ODBC call failed error when an access table is opened

I have linked the sql server tables to ms access so that I can use ms access as the front end.I was able to access the tables from access, until I run into an error ODBC call failed when I tried to open one of the tables. There was no problem with the other tables. Actually I have changed a column name in sql server after creating a link. Is this the problem? I am really worried about this as I was about to use access as a front-end for my future purposes.
When you link to a remote table, Access stores metadata about that table. When you later change the table structure, the metadata doesn't get updated to capture the change.
Delete the link. Then recreate the link. That way the metadata will be consistent with the current version of the table.
Yes changing the column name after linking the table is most likely causing your failure. Is it is now trying to pull data from a column that no longer exists. You will need to relink the table. You can programatically link tables in access. We do that in may of our access applications and drive the tables that need to be linked from a local access table.
Public Sub LinkODBCTables()
Dim objRS As DAO.Recordset
Dim objTblDef As DAO.TableDef
Dim strTableName As String
Dim strAliasName As String
Dim strDSN As String
Dim lngTblCount As Long
Set objRS = CurrentDb.OpenRecordset( _
" select TableName," & _
" AliasName," & _
" DSN," & _
" DatabaseName," & _
" Development_DSN," & _
" UniqueIndexCol" & _
" from tblODBCLinkedTables " & _
" order by TableName", dbOpenSnapshot)
While Not objRS.EOF
' Check to see if we already have this linked tableDef
' We don't care if it is not actually in there
strTableName = objRS.Fields("TableName")
If Not IsNull(objRS.Fields("AliasName")) Then
strAliasName = objRS.Fields("AliasName")
Else
strAliasName = strTableName
End If
If DEV_MODE Then
strDSN = objRS.Fields("Development_DSN")
Else
strDSN = objRS.Fields("DSN")
End If
On Error Resume Next
CurrentDb.TableDefs.Delete strAliasName
If Err.Number <> 0 And _
Err.Number <> 3265 Then ' item not found in collection
Dim objError As Error
MsgBox "Unable to delete table " & strAliasName
MsgBox Err.Description
For Each objError In DBEngine.Errors
MsgBox objError.Description
Next
End If
On Error GoTo 0
Set objTblDef = CurrentDb.CreateTableDef(strAliasName)
objTblDef.Connect = g_strSQLServerConn & _
"DSN=" & strDSN & _
";DATABASE=" & objRS.Fields("DatabaseName") & _
";UID=" & g_strSQLServerUid & _
";PWD=" & g_strSQLServerPwd
objTblDef.SourceTableName = strTableName
On Error Resume Next
CurrentDb.TableDefs.Append objTblDef
If Err.Number <> 0 Then
Dim objErr As DAO.Error
For Each objErr In DBEngine.Errors
MsgBox objErr.Description
Next
End If
On Error GoTo 0
' Attempt to create a uniqe index of the link for updates
' if specified
If Not IsNull(objRS.Fields("UniqueIndexCol")) Then
' Execute DDL to create the new index
CurrentDb.Execute " Create Unique Index uk_" & strAliasName & _
" on " & strAliasName & "(" & objRS.Fields("UniqueIndexCol") & ")"
End If
objRS.MoveNext
Wend
objRS.Close
End Sub
We are using a single SQLServer login for our access applications so the g_strSQLServerUID and g_strSQLServerPwd are globals that contain that info. You may need to tweek that for your own needs or integrated security. We are setting up two DSNs one for production and the other for development. The DEV_MODE global controls wich set of DSNs are linked. You can call this code from a startup macro or startup form. It will deleted the old link and create a new link so you always have the most up to date schema.