Code to accompany the Relationship Report with Field Information article. Last updated April 2010.
'Purpose: Show additional information beside each field in the Print Relationships report. 'Author: Allen Browne. allen@allenbrowne.com. June 2006. 'Usage: Set the On Click property of a command button to: ' =RelReport() 'Method: The Relationships report uses a list box for each table. ' We open the report, switch to design view, and change the RowSource of each list box, ' to give more detailed information on each field, by adding the codes below to each field. ' These codes are added to the field names in the Relationships report: ' Field Types: ' =========== ' A AutoNumber field (size Long Integer)+ ' Att Attachment (always with the X prefix.) ' Bin Binary (not available in the interface.) ' B Byte (Number) ' C Currency ' Dbl Double (Number) ' Dec Decimal (Number) ' Dt Date/Time ' Guid Replication ID (Globally Unique IDentifier) ' Hyp Hyperlink ' Int Integer (Number) ' L Long Integer (Number) ' M Memo field ' Ole OLE Object ' Sng Single (Number) ' T Text, with number of characters (size) ' Tf Text, fixed width, with number of characters (not available in the interface.) ' Yn Yes/No ' ? Unknown field type ' X prefix indicates a Complex data type, e.g. XL = Complex Long, XTf = Complext Text fixed-width. ' Indexes: ' ======= ' P Primary Key ' U Unique Index ('No Duplicates') ' I Indexed ('Duplicates Ok') ' Note: Lower case p, u, or i indicates a secondary field in a multi-field index. ' Properties: ' ========== ' D Default Value set. ' R Required property is Yes ' V Validation Rule set. ' Z Allow Zero-Length is Yes (Text, Memo and Hyperlink only.) Option Compare Database Option Explicit Public Function RelReport(Optional bSetMarginsAndOrientation As Boolean = True) As Long 'On Error GoTo Err_Handler 'Purpose: Main routine. Opens the relationships report with extended field information. 'Author: Allen Browne. allen@allenbrowne.com. January 2006. 'Argument: bSetMarginsAndOrientation = False to NOT set margins and landscape. 'Return: Number of tables adjusted on the Relationships report. 'Notes: 1. Only tables shown in the Relationships diagram are processed. ' 2. The table's record count is shown in brackets after the last field. ' 3. Aliased tables (typically duplicate copies) are not processed. ' 4. System fields (used for replication) are suppressed. ' 5. Setting margins and orientation operates only in Access 2002 and later. Dim db As DAO.Database 'This database. Dim tdf As DAO.TableDef 'Each table referenced in the Relationships window. Dim ctl As Control 'Each control on the report. Dim lngKt As Long 'Count of tables processed. Dim strReportName As String 'Name of the relationships report Dim strMsg As String 'MsgBox message. 'Initialize: Open the Relationships report in design view. Set db = CurrentDb() strReportName = OpenRelReport(strMsg) If strReportName <> vbNullString Then 'Loop through the controls on the report. For Each ctl In Reports(strReportName).Controls If ctl.ControlType = acListBox Then 'Set the TableDef based on the Caption of the list box's attached label. If TdfSetOk(db, tdf, ctl, strMsg) Then 'Change the RowSource to the extended information ctl.RowSource = DescribeFields(tdf) lngKt = lngKt + 1& 'Count the tables processed successfully. End If End If Next 'Results If lngKt = 0& Then 'Notify the user if the report did not contain the expected controls. strMsg = strMsg & "Diagram of tables not found on report " & strReportName & vbCrLf Else 'Preview the report. Reports(strReportName).Section(acFooter).Height = 0& DoCmd.OpenReport strReportName, acViewPreview 'Reduce margins and switch to landscape (Access 2002 and later only.) If bSetMarginsAndOrientation Then Call SetMarginsAndOrientation(Reports(strReportName)) End If End If End If Exit_Handler: 'Show any message. If strMsg <> vbNullString Then MsgBox strMsg, vbInformation, "Relationships Report (adjusted)" End If 'Clean up Set ctl = Nothing Set db = Nothing 'Return the number of tables processed. RelReport = lngKt Exit Function Err_Handler: strMsg = strMsg & "RelReport: Error " & Err.Number & ": " & Err.Description & vbCrLf Resume Exit_Handler End Function Private Function OpenRelReport(strErrMsg As String) As String On Error GoTo Err_Handler 'Purpose: Open the Relationships report. 'Return: Name of the report. Zero-length string on failure. 'Argument: String to append any error message to. Dim iAccessVersion As Integer 'Access version. iAccessVersion = Int(Val(SysCmd(acSysCmdAccessVer))) Select Case iAccessVersion Case Is < 9 strErrMsg = strErrMsg & "Requires Access 2000 or later." & vbCrLf Case 9 RunCommand acCmdRelationships SendKeys "%FR", True 'File | Relationships. RunCommand acCmdPrintRelationships is not in A2000. RunCommand acCmdDesignView Case Is > 9 RunCommand acCmdRelationships RunCommand 483 ' acCmdPrintRelationships RunCommand acCmdDesignView End Select 'Return the name of the last report opened OpenRelReport = Reports(Reports.Count - 1&).Name Exit_Handler: Exit Function Err_Handler: Select Case Err.Number Case 2046& 'Relationships window is already open. 'A2000 cannot recover, because SendKeys requires focus on the window. If iAccessVersion > 9 Then Resume Next Else strErrMsg = strErrMsg & "Close the relationships window, and try again." & vbCrLf Resume Exit_Handler End If Case 2451&, 2191& 'Report not open, or not open in design view. strErrMsg = strErrMsg & "The Relationships report must be open in design view." & vbCrLf Resume Exit_Handler Case Else strErrMsg = strErrMsg & "Error " & Err.Number & ": " & Err.Description & vbCrLf Resume Exit_Handler End Select End Function Private Function TdfSetOk(db As DAO.Database, tdf As DAO.TableDef, ctl As Control, strErrMsg As String) As Boolean On Error GoTo Err_Handler 'Purpose: Set the TableDef passed in, using the name in the Caption in the control's attached label. 'Return: True on success. (Fails if the caption is an alias.) 'Arguments: db = database variable (must already be set). ' tdf = the TableDef variable to be set. ' ctl = the control that has the name of the table in its attached label. ' strMsg = string to append any error messages to. Dim strTable As String 'The name of the table. strTable = ctl.Controls(0).Caption 'Get the name of the table from the attached label's caption. Set tdf = db.TableDefs(strTable) 'Fails if the caption is an alias. TdfSetOk = True 'Return true if it all worked. Exit_Handler: Exit Function Err_Handler: Select Case Err.Number Case 3265& 'Item not found in collection. (Table name is an alias.) strErrMsg = strErrMsg & "Skipped table " & strTable & vbCrLf Case Else strErrMsg = strErrMsg & "Error " & Err.Number & ": " & Err.Description & vbCrLf End Select Resume Exit_Handler End Function Private Function DescribeFields(tdf As DAO.TableDef) As String 'Purpose: Loop through the fields of the table, to create a string _ to use as the RowSource of the list box (Value List type). 'Note: We use literals instead of constants for the data types that do not exist before A2007. Dim fld As DAO.Field 'Each field of the table. Dim strType As String Dim strReturn As String 'String to build up and return. For Each fld In tdf.Fields 'Skip replication info fields. If (fld.Attributes And dbSystemField) = 0& Then Select Case CLng(fld.Type) Case dbText strType = IIf((fld.Attributes And dbFixedField) = 0&, "T", "Tf") & fld.Size & _ IIf(fld.AllowZeroLength, "Z", vbNullString) strReturn = strReturn & DescribeFieldSub(tdf, fld, strType) Case 109& 'dbComplexText strType = IIf((fld.Attributes And dbFixedField) = 0&, "T", "Tf") & fld.Size & _ IIf(fld.AllowZeroLength, "Z", vbNullString) strReturn = strReturn & DescribeFieldSub(tdf, fld, strType, True) Case dbMemo strType = IIf((fld.Attributes And dbHyperlinkField) = 0&, "M", "Hyp") & _ IIf(fld.AllowZeroLength, "Z", vbNullString) strReturn = strReturn & DescribeFieldSub(tdf, fld, strType) Case dbLong strType = IIf((fld.Attributes And dbAutoIncrField) = 0&, "L", "A") strReturn = strReturn & DescribeFieldSub(tdf, fld, strType) Case 104& 'dbComplexLong strReturn = strReturn & DescribeFieldSub(tdf, fld, "L", True) Case dbInteger strReturn = strReturn & DescribeFieldSub(tdf, fld, "Int") Case 103& 'dbComplexInteger strReturn = strReturn & DescribeFieldSub(tdf, fld, "Int", True) Case dbCurrency strReturn = strReturn & DescribeFieldSub(tdf, fld, "C") Case dbDate strReturn = strReturn & DescribeFieldSub(tdf, fld, "Dt") Case dbDouble strReturn = strReturn & DescribeFieldSub(tdf, fld, "Dbl") Case 106& 'dbComplexDouble strReturn = strReturn & DescribeFieldSub(tdf, fld, "Dbl", True) Case dbSingle strReturn = strReturn & DescribeFieldSub(tdf, fld, "Sng") Case 105& 'dbComplexSingle strReturn = strReturn & DescribeFieldSub(tdf, fld, "Sng", True) Case dbByte strReturn = strReturn & DescribeFieldSub(tdf, fld, "B") Case 102& 'dbComplexByte strReturn = strReturn & DescribeFieldSub(tdf, fld, "B", True) Case dbDecimal strReturn = strReturn & DescribeFieldSub(tdf, fld, "Dec") Case 108& 'dbComplexDecimal strReturn = strReturn & DescribeFieldSub(tdf, fld, "Dec", True) Case dbBoolean strReturn = strReturn & DescribeFieldSub(tdf, fld, "Yn") Case dbLongBinary strReturn = strReturn & DescribeFieldSub(tdf, fld, "Ole") Case dbGUID strReturn = strReturn & DescribeFieldSub(tdf, fld, "Guid") Case 107& 'dbComplexGUID strReturn = strReturn & DescribeFieldSub(tdf, fld, "Guid", True) Case 101& 'dbAttachment strReturn = strReturn & DescribeFieldSub(tdf, fld, "Att", True) Case dbBinary strReturn = strReturn & DescribeFieldSub(tdf, fld, "Bin") Case Else strReturn = strReturn & DescribeFieldSub(tdf, fld, "?") End Select End If Next DescribeFields = strReturn & """ (" & DCount("*", tdf.Name) & ")""" End Function Private Function IsCalcTableField(fld As DAO.Field) As Boolean 'Purpose: Returns True if fld is a calculated field (Access 2010 and later only.) On Error GoTo ExitHandler Dim strExpr As String strExpr = fld.Properties("Expression") If strExpr <> vbNullString Then IsCalcTableField = True End If ExitHandler: End Function Private Function DescribeFieldSub(tdf As TableDef, fld As Field, strTypeDescrip As String, Optional bIsComplex As Boolean) As String Dim strOut As String Const strcSep = ";" 'Separator between items in the list box. 'strOut = IIf(bIsComplex, """X", """") & fld.Name & " " & strTypeDescrip strOut = """" & fld.Name & " " If bIsComplex Then strOut = strOut & "X" End If If IsCalcTableField(fld) Then strOut = strOut & "*" End If strOut = strOut & strTypeDescrip If fld.Required Then 'Required? strOut = strOut & "R" End If 'Validation Rule? If fld.ValidationRule <> vbNullString Then strOut = strOut & "V" End If 'Default Value? If fld.DefaultValue <> vbNullString Then strOut = strOut & "D" End If strOut = strOut & DescribeIndexField(tdf, fld.Name) & """" & strcSep If bIsComplex Then If fld.Type = 101 Then 'Attachment strOut = strOut & """ (" & fld.Name & ".FileData)""" & strcSep & _ """ (" & fld.Name & ".FileName)""" & strcSep & _ """ (" & fld.Name & ".FileType)""" & strcSep Else strOut = strOut & """ (" & fld.Name & ".Value)" & """" & strcSep End If End If DescribeFieldSub = strOut End Function Private Function DescribeIndexField(tdf As DAO.TableDef, strField As String) As String 'Purpose: Indicate if the field is part of a primary key or unique index. 'Return: String containing "P" if primary key, "U" if uniuqe index, "I" if non-unique index. ' Lower case letters if secondary field in index. Can have multiple indexes. 'Arguments: tdf = the TableDef the field belongs to. ' strField = name of the field to search the Indexes for. Dim ind As DAO.Index 'Each index of this table. Dim fld As DAO.Field 'Each field of the index Dim iCount As Integer Dim strReturn As String 'Return string For Each ind In tdf.Indexes iCount = 0 For Each fld In ind.Fields If fld.Name = strField Then If ind.Primary Then strReturn = strReturn & IIf(iCount = 0, "P", "p") ElseIf ind.Unique Then strReturn = strReturn & IIf(iCount = 0, "U", "u") Else strReturn = strReturn & IIf(iCount = 0, "I", "i") End If End If iCount = iCount + 1 Next Next DescribeIndexField = strReturn End Function Private Function SetMarginsAndOrientation(obj As Object) As Boolean 'Purpose: Set half-inch margins, and switch to landscape orientation. 'Argument: the report. (Object used, because Report won't compile in early versions.) 'Return: True if set. 'Notes: 1. Applied in Access 2002 and later only. ' 2. Setting orientation in design view and then opening in preview does not work reliably. Const lngcMargin = 720& 'Margin setting in twips (0.5") 'Access 2000 and earlier do not have the Printer object. If Int(Val(SysCmd(acSysCmdAccessVer))) >= 10 Then With obj.Printer .TopMargin = lngcMargin .BottomMargin = lngcMargin .LeftMargin = lngcMargin .RightMargin = lngcMargin .Orientation = 2 'acPRORLandscape not available in A2000. End With 'Return True if set. SetMarginsAndOrientation = True End If End Function
Home | Index of tips | Top |