BusinessObjects Board

List of objects with SQL used in a report

I’ve used Dave’s code to identify the list of objects used in a report (and if I could figure out how to link to the download, I would place that link here).

I know almost nothing about VBA so it’s a miracle that I got Dave’s code to work for me. My question is “How would I modify the code to obtain the object’s SQL as well?” I am mainly interested in the Select but some of our objects have Wheres also (and I guess the conditions have Wheres instead of Selects!). TIA :slight_smile:


KSG :us: (BOB member since 2002-07-17)

Karen:

What I have done is create a free-hand SQL data provider in the same document that holds the macro code. Then you can query the repository (using ManagerO or equivalent) to get the class and object and SQL. By linking the data providers, you can show the SQL for each object along with the other information.


Dave Rathbun :us: (BOB member since 2002-06-06)

This worked great, thanks!

Another question: Is there a way to get the contents of the SQL window (whatever it’s called)? That is, to retrieve the entire SQL statement generated by the data provider?


KSG :us: (BOB member since 2002-07-17)

There is a function called DataProviderSQL() that will allow you to do that in any BusinessObjects report. Is that what you are looking for? That would be in a Variable, not in VBA code…


Dave Rathbun :us: (BOB member since 2002-06-06)

I would want to use it along with your code to look at objects (or in some cases, instead of the objects), to scan about 3,700 documents. Is there a way to get this into VBA? I would need see-spot-run instructions.

Thanks!


KSG :us: (BOB member since 2002-07-17)

I don’t have time at the moment to write “see spot run” instructions, but based on Dave’s code the dpMyDataProvider.SQL property will return the full SQL, I believe.


Dwayne Hoffpauir :us: (BOB member since 2002-09-19)

thanks, Dwayne! I will try to muddle through with this.


KSG :us: (BOB member since 2002-07-17)

Okay, so Dwayne as provided the property of the data provider to get the SQL. However, that will be the entire data provider SQL, not SQL for an individual object. If you look at the code posted in BOB’s Downloads, you’ll see this:

                If dpMyDataProvider.GetType = "DPQTC" Then  ' This is a dataprovider from a universe 
                
                    ' Get the universe associated with the data provider 
                    strUniverseName = dpMyDataProvider.UniverseName 
                    
                    ' Next step will be to parse the data provideres and write out to 
                    ' an output file the comma separated list of objects used 
                    Open strObjList For Append As OutNum 
                    
                    For k = 1 To dpMyDataProvider.Columns.Count 
                    
                        Print #OutNum, strUniverseName; ", "; strCurrentFile; ", "; dpMyDataProvider.Columns.Item(k).Name; ", "; "X" 

                    Next k 
                    
                    Close OutNum 
                    
                End If 

That is the code that scans each data provider and writes out the CSV file that is later used for input to the document. If you want to add a capture for the SQL for each data provider (which should only be done once per DP, not once per object) I would add the following code just before the “End If” posted above:

                    Open strDPList For Append As OutNum 
                    
                        Print #OutNum, strUniverseName; ", "; strCurrentFile; ", "; dbMyDataProvider.Name; ","; dpMyDataProvider.SQL 
                    
                    Close OutNum 

Somewhere earlier in the code you would define strDPList as a new output file to store the SQL in. Look for the definition of strObjList and copy that line, and assign a new filename to the DPList. That will give you a second file that will list the universe name, document name, data provider name, and SQL used for each data provider.

I didn’t test any of this code, so you might have to play a bit to get it to work. Good luck!


Dave Rathbun :us: (BOB member since 2002-06-06)

Dwayne and Dave, thank you both for your help.

It took me a very long time to get to this but I finally did and I was actually able to get it to work! :smiley:

This is the end of a project I’ve been working on. I relied almost exclusively on things I’ve learned from BOB, the project was a success and now my boss thinks I’m very clever :wink: although I did tell him I had help from :mrgreen:


KSG :us: (BOB member since 2002-07-17)

Open strDPList For Append As OutNum
Print #OutNum, strUniverseName; ", "; strCurrentFile; ", "; dpMyDataProvider.Name; ","; dpMyDataProvider.SQL
Close OutNum

I added this code, but the output in the CSV file is not in the format I would like it: The SQL code of each data provider is split into individual rows. What I would like instead is the complete SQL code for one data provider all in ONE MS Excel cell.

As is:

Cash	 AR Promised Report.rep	 Query 1 for BSP
SELECT		
  SYSADM.PS_BUS_UNIT_TBL_GL.BUSINESS_UNIT		
  SYSADM.PS_ZTR_BNK_SDST_VW.BANK_ACCOUNT_NUM	
  SYSADM.PS_ZTR_BNK_SDST_VW.RECON_BANK_DT		
  SYSADM.PS_ZTR_BNK_SDST_VW.RECON_TRAN_AMT * SYSADM.PS_ZTR_BNK_SDST_VW.SIGN_ADJUSTER		
FROM

These are seven individual rows in the MS Excel spreadsheet/csv file, which makes it really hard to use it as a data provider :nonod:

This is caused by the commas within the SQL statement: MS Excel interprets this as a new field. It seems that I have to enclose the SQL code in double quotes, how do I define a constant string variable to be a double-quote? I need to embed the string dpMyDataProvider.Name with double quotes.

Me being a bloody VBA novice can anybody help me with this excercise :?:


Andreas :de: (BOB member since 2002-06-20)

I made this work:

Open strDPList For Append As OutNum
Print #OutNum, strUniverseName; ", "; strCurrentFile; ", "; dpMyDataProvider.Name; ","; Chr(34) & dpMyDataProvider.SQL & Chr(34)
Close OutNum

The complete modified code (creates an additional file called “ReportDictionary_SQLUsed.csv”, which contains universe name, report name, data provider name and data provider SQL):

' "ObjectsUsed"
' Dave Rathbun, Integra Solutions
' www.IntegraSolutions.net
'
' modified by Andreas Schneider, Cardinal Solutions Group
' www.CardinalSolutions.com
' to include SQL code of data providers
'
' provided "as is", no warranty implied
' This macro does not by design to anything to the reports that it reads. It does write
' to a CSV file that will be - by default - located in the "UserDocs" folder as specified by
' a user preference setting. The data provider for the document must point to this file. As of
' this revision, the user must do this step manually.

Option Explicit                             ' Force explicit declaration of all variables

' GLOBAL CONST *******************************************************
' Standard File I/O numbers assigned here
Global Const ErrNum As Integer = 1
Global Const OutNum As Integer = 2

Sub ObjectsUsed()
    Dim strCurrentFile As String
    Dim docMyDocument As Document
    Dim dpMyDataProvider As DataProvider
    Dim dpColumn As Column
    Dim strUniverseName As String
    Dim strUniverseDomain As String
    
    Dim strNextItem As String   ' used to parse comma listed table names
    Dim strDocPath As String    ' Path to use to look for files
    Dim strObjList As String    ' Path for output file
    Dim strDPList  As String    ' Path for output file
    Dim strErrorLog As String   ' Path for error log
    
    Dim i, j, k As Integer      ' loop counters
    Dim x As Integer            ' function result value
    
    ' Start directory list of .rep files in the standard file location. If you want to load files
    ' from a different directory, you would hard code the value below. Make sure that a hard-coded
    ' path includes the trailing back slash!
    
    strDocPath = Application.GetInstallDirectory(boDocumentDirectory) & "\"
    strObjList = strDocPath & "ReportDictionary_ObjectsUsed.csv"
    strDPList = strDocPath & "ReportDictionary_SQLUsed.csv"
    strErrorLog = strDocPath & "ReportDictionary_ObjectsUsed.log"
         
    If FileExist(strObjList) Then
        Kill (strObjList)
    End If
    If FileExist(strErrorLog) Then
        Kill (strErrorLog)
    End If
    
    strCurrentFile = Dir(strDocPath & "*.rep")
    
    Open strObjList For Append As OutNum
    Print #OutNum, "Universe(Domain)"; ", "; "Report Name"; ", "; "Object Name"; ", X"
    Close #OutNum
    
    Open strDPList For Append As OutNum
    Print #OutNum, "Universe(Domain)"; ", "; "Report Name"; ", "; "DP Name"; ", SQL Code"
    Close #OutNum
    
    ' Do while there are reports we have not looked at
    Do While strCurrentFile <> ""
     
      If strCurrentFile <> ThisDocument.Name &amp; ".rep" Then

        Set docMyDocument = Application.Documents.Open(strDocPath &amp; strCurrentFile)
        
        If docMyDocument.Name <> "" Then
        
            For j = 1 To docMyDocument.DataProviders.Count
            
                Set dpMyDataProvider = docMyDocument.DataProviders.Item(j)
                If dpMyDataProvider.GetType = "DPQTC" Then  ' This is a dataprovider from a universe
                
                    ' Get the universe associated with the data provider
                    strUniverseName = dpMyDataProvider.UniverseName
                    ' strUniverseDomain = dpMyDataProvider.Universe.DomainName
                    ' strUniverseName = strUniverseName &amp; "(" &amp; strUniverseDomain &amp; ")"
                    
                    ' Next step will be to parse the data provideres and write out to
                    ' an output file the comma separated list of objects used
                    Open strObjList For Append As OutNum
                    
                    For k = 1 To dpMyDataProvider.Columns.Count
                        Print #OutNum, strUniverseName; ", "; strCurrentFile; ", "; dpMyDataProvider.Columns.Item(k).Name; ", "; "X"
                    Next k
                    
                    Close OutNum
                    
                    Open strDPList For Append As OutNum
                         Print #OutNum, strUniverseName; ", "; strCurrentFile; ", "; dpMyDataProvider.Name; ","; Chr(34) &amp; dpMyDataProvider.SQL &amp; Chr(34)
                    Close OutNum
                    
                End If
            
            Next j  ' Next data provider, if any more
            
        End If
        
        docMyDocument.Close
        
      End If
        
      ' Get next document, if there is one.
      strCurrentFile = Dir
   Loop
   
   ThisDocument.Activate
   ThisDocument.Refresh

End Sub

'*********************************************************************
' This subroutine is used to log entries to a specified log file. The
' only error that is not logged here is a failure to write to the log
' file...
'*********************************************************************
Sub LogError(ErrorString$)

    Dim strErrorLog As String
    strErrorLog = Application.GetInstallDirectory(boDocumentDirectory) &amp; "\ReportDictionary_ObjectsUsed.log"
    Open strErrorLog For Append As #ErrNum
    Print #ErrNum, Date; " "; Time(); " > "; ErrorString$
    Close #ErrNum

End Sub ' LogError

'*********************************************************************
' This function is used to check for the existance of a file. It returns
' a fake "boolean" flag using the BusinessObjects constants TRUE and FALSE.
' If the file is found, the function returns TRUE. Otherwise, the function
' returns FALSE.
'
' If any file name is not found, an entry is made to the LogFile.
'*********************************************************************
Function FileExist(myFileName$)

  ' First Check to ensure that myFileName is a valid file

  On Error GoTo NotFound            ' provide alternate error handling

  Open myFileName$ For Input As 255 ' attempt to open file
  On Error Resume Next              ' if success, restore default error handling
  Close 255                         ' close file (it was only a test, afterall)

  FileExist = True
  
  Exit Function
  
NotFound:
  On Error Resume Next
  FileExist = False
  LogError ("File " &amp; myFileName$ &amp; " not found.")
  
End Function ' File Exist

Thank you Dave :yesnod:


Andreas :de: (BOB member since 2002-06-20)

Another question:
I noticed that the above code only includes RESULT objects, but not universe objects used as a condition.

Can anyone cue me on how to reference the universe objects used in a condition of a data provider (if there is such a method/property etc.)?


Andreas :de: (BOB member since 2002-06-20)

Try this. I have several improvements on my “I’ll get to it someday” list, but that post should still nudge you in the right direction.


Dwayne Hoffpauir :us: (BOB member since 2002-09-19)

Another problem:
I am reporting on the SQL code of data providers using VBA

Print #OutNum, strUniverseName; ", "; strCurrentFile; ", "; dpMyDataProvider.Name; ","; Chr(34) &amp; dpMyDataProvider.SQL &amp; Chr(34)

I have some data providers with SQL code longer than about 2443 characters. When I look directly at the CSV output file the SQL shows up correctly in full length, but when I use that CSV as a personal data provider for a report, Business Objects seems to cut off the text field containing the SQL code after approx. 2443 characters.

Does anybody have an explanation, or has anybody encountered something similar? If so, what is the work around?


Andreas :de: (BOB member since 2002-06-20)

It’s probably a limitation of the CSV import process for BusinessObjects. I haven’t had time to set up some test data to help you out, although I had intended to. Just too many other things going on right now.

Which version of BusinessObjects are you using for this?


Dave Rathbun :us: (BOB member since 2002-06-06)

I am using Business Objects v5.1.7


Andreas :de: (BOB member since 2002-06-20)

I’ve adapted Dave’s excellent code to search sub folders and include objects used in the conditions.

It requires a reference to Microsoft Scripting Runtime - in the VBA editor, select Tools/References and tick the Microsoft Scripting Runtime.


'requires a reference to Microsoft Scripting Runtime
' - select Tools/References from the menu
' find Microsoft Scripting Runtime and TICK the box.
' if you don't have it listed, it's available as a free download from
' www.microsoft.com

'Modified by Chris Hogben
'Modifications: -
'   Searches sub folders
'   includes objects used in conditions if they aren't returned
'   lists path and filename seperately
'   lists document author and the last time the report was modified
'   changed some of the loops to use for each
'   changed the code to check if a file exists
'   will save the results and errors in the 'root' folder
'   deletes the error file
'   set application.interactive to false to avoid the program pausing because of obsolete objects
'   if there's an error opening the document - then the program will log it and move on to the next file
'   changed the name of the error log
'

' "ObjectsUsed"
' Dave Rathbun, Integra Solutions
' www.IntegraSolutions.net
' provided "as is", no warranty implied
' This macro does not by design to anything to the reports that it reads. It does write
' to a CSV file that will be - by default - located in the "UserDocs" folder as specified by
' a user preference setting. The data provider for the document must point to this file. As of
' this revision, the user must do this step manually.




Option Explicit                             ' Force explicit declaration of all variables

' GLOBAL CONST *******************************************************
' Standard File I/O numbers assigned here
Global Const ErrNum As Integer = 1
Global Const OutNum As Integer = 2

Private m_strDocPath As String
Private m_strObjList As String
Private m_strDPList As String
Private m_strErrorLog As String
Private m_strThisDocumentName As String
   

Public Sub RecurseAllSubFoldersStart()
    

    
  
    m_strDocPath = InputBox(prompt:="Please enter the root path", _
                            Default:="c:\")
    
    
    
    If Len(m_strDocPath) = 0 Then
        MsgBox "aborted"
        Exit Sub
    Else
        If Right$(m_strDocPath, 1) <> "\" Then
            m_strDocPath = m_strDocPath &amp; "\"
        End If
        If Len(Dir(m_strDocPath)) = 0 Then
            MsgBox "Cannot find folder, aborted"
            Exit Sub
        End If
    End If
    Application.Interactive = False
    
    m_strObjList = m_strDocPath &amp; "ReportDictionary_ObjectsUsed.csv"
    m_strDPList = m_strDocPath &amp; "ReportDictionary_SQLUsed.csv"
    m_strErrorLog = m_strDocPath &amp; "ReportDictionary_Error.log"
    m_strThisDocumentName = ThisDocument.Name &amp; ".rep"
    
    
    'set up output files
    If Len(Dir(m_strObjList)) <> 0 Then
        Kill (m_strObjList)
    End If
    If Len(Dir(m_strObjList)) <> 0 Then
        Kill (m_strErrorLog)
    End If
    If Len(Dir(m_strDPList)) <> 0 Then
        Kill (m_strDPList)
    End If
    
    Open m_strObjList For Append As OutNum
    'Print #OutNum, "Universe(Domain)"; ", "; "Report Name"; ", "; "Object Name"
    Print #OutNum, "Report Path,Report Name,Author,Date Last Modified, Universe,Object"
    Close #OutNum
    
    
    
    
    
    Open m_strDPList For Append As OutNum
    Print #OutNum, "Universe(Domain)"; ", "; "Report Name"; ", "; "DP Name"; ", SQL Code"
    Close #OutNum
    
    RecurseFolders strFoldername:=m_strDocPath

    Application.Interactive = True
End Sub

Private Function RecurseFolders(ByVal strFoldername As String) As Boolean
Dim objFSO As FileSystemObject
Dim objFolder As Folder
Dim objFiles As Files
Dim objFile As File
Dim strFileName As String
Dim objSubFolders As Folders
Dim objSubFolder As Folder
Dim strFolderpath As String
    On Error Resume Next
    Set objFSO = New FileSystemObject
        
    If Err.Number > 0 Then
        RecurseFolders = False
        Exit Function
    End If
    
    On Error GoTo 0
    
    If objFSO.FolderExists(strFoldername) Then
        
        Set objFolder = objFSO.GetFolder(strFoldername)
        Set objSubFolders = objFolder.SubFolders
        
        For Each objSubFolder In objSubFolders
            RecurseFolders (objSubFolder.Path)
        Next
        
        strFolderpath = objFolder.Path
        If Right$(strFolderpath, 1) <> "\" Then
            strFolderpath = strFolderpath &amp; "\"
        End If
        
        For Each objFile In objFolder.Files
            With objFile
                strFileName = LCase$(.Name)
                If Right$(strFileName, 3) = "rep" Then
                
                    If objFile.Name <> m_strThisDocumentName Then
                        'Debug.Print objFile.Path
                        
                        ObjectsUsed strPath:=strFolderpath, _
                                    strFileName:=.Name, _
                                    strDateLastModified:=Format$(.DateLastModified, "dd/mm/yyyy")
                    End If
                End If
            End With
        Next
        
        Set objFolder = Nothing
        Set objSubFolder = Nothing
        Set objFile = Nothing
        
        
    Else
        RecurseFolders = False
    End If
    
    Set objFSO = Nothing

End Function

Private Sub ObjectsUsed(ByVal strPath As String, ByVal strFileName As String, ByVal strDateLastModified As String)
Dim docMyDocument As Document
Dim dpMyDataProvider As DataProvider
Dim objColumn As Column
Dim strUniverseName As String
Dim strThisDocumentName As String
Dim objCondition As Condition
Dim objQuery As Query
Dim strColumns As String
Dim strConditionObject As String
        
        
        On Error Resume Next
        
        Set docMyDocument = Application.Documents.Open(strPath &amp; strFileName, True, True)
        If Err.Number <> 0 Then
            Debug.Print "unable to open " &amp; strPath And strFileName
            LogError "unable to open " &amp; strPath And strFileName
            Exit Sub
        End If
        On Error GoTo 0
        If Len(docMyDocument.Name) <> 0 Then
        
       
            For Each dpMyDataProvider In docMyDocument.DataProviders
            

                If dpMyDataProvider.GetType = "DPQTC" Then  ' This is a dataprovider from a universe
                
                    ' Get the universe associated with the data provider
                    strUniverseName = dpMyDataProvider.UniverseName
                    
                    ' Next step will be to parse the data providers and write out to
                    ' an output file the comma separated list of objects used
                    Open m_strObjList For Append As OutNum
                    
                    'data provider objects used as results
                    strColumns = "" ' build up a string of columns used as results
                    For Each objColumn In dpMyDataProvider.Columns
                        strColumns = strColumns &amp; "," &amp; objColumn.Name
                       
                        Print #OutNum, strPath &amp; "," &amp; strFileName &amp; "," &amp; docMyDocument.Author &amp; "," &amp; strDateLastModified &amp; "," &amp; strUniverseName &amp; "," &amp; objColumn.Name
                        
                    Next
                    
                    'data provider objects used as conditions
                    
                    For Each objQuery In dpMyDataProvider.Queries
                        For Each objCondition In objQuery.Conditions
                            
                            strConditionObject = objCondition.Object
                            'check if we've already output this object by checking the strColumns string
                            If InStr("," &amp; strConditionObject, strColumns) = 0 Then
                                'condition object isn't a result object as well - output it
    
                                Print #OutNum, strPath &amp; "," &amp; strFileName &amp; "," &amp; docMyDocument.Author &amp; "," &amp; strDateLastModified &amp; "," &amp; strUniverseName &amp; "," &amp; strConditionObject
                            End If
                        Next
                    Next
                    
                
                    
                    Close OutNum
                    
                    Open m_strDPList For Append As OutNum
                         Print #OutNum, strUniverseName; ", "; strFileName; ", "; dpMyDataProvider.Name; ","; Chr(34) &amp; dpMyDataProvider.SQL &amp; Chr(34)
                    Close OutNum
                    
                End If
            
            Next   ' Next data provider, if any more
            
        End If
        
        docMyDocument.Close
        
      
        
    
    
   'ThisDocument.Activate



    Set objColumn = Nothing
    Set objCondition = Nothing
    Set objQuery = Nothing
    Set docMyDocument = Nothing
    Set dpMyDataProvider = Nothing
   


End Sub

'*********************************************************************
' This subroutine is used to log entries to a specified log file. The
' only error that is not logged here is a failure to write to the log
' file...
'*********************************************************************
Sub LogError(ByVal ErrorString As String)

    
    Open m_strErrorLog For Append As #ErrNum
    Print #ErrNum, Now(); " > "; ErrorString$
    Close #ErrNum

End Sub

Hope it’s useful
:smiley:


chrishogben (BOB member since 2003-06-26)

I’m sure it will be. :slight_smile:

Another note… I rewrote the program to use a VBA data provider instead of a CSV file. (Andreas, let me know if you want to try that one instead to get around the length problem you’re having.) The idea was that instead of capturing the data to a text file, and then having to link the document data provider to that text file (it all seemed so fragile that way) the processing, data, and end results would all be completely self contained.

Meaning you can drop the .REP file anywhere and run it, and no path names would be involved. 8)

It works very well for smaller sets of reports. However, I had a client that tried using the new version against several thousand documents, and it started throwing all sorts of memory errors. My assumption is either that (a) I did something wrong in the code, or (b) once you get passed a certain threshold a VBA data provider becomes more difficult to manage.

I gave that client the “original” version of the code (using external CSV file) and it worked fine. Got through every document on the drive. So while the VBA data provider version is cleaner and easier to manage, it has problems with “large” sets of data.


Dave Rathbun :us: (BOB member since 2002-06-06)

Yes, Dave I would like to try the VBA data provider approach, just to see if I am still encountering problems with text fields larger than 2443 characters, please.


Andreas :de: (BOB member since 2002-06-20)

I apparently have to find it first. :shock: I’ll either mail it to you directly, or posted it as a supplement to the BOB’s Downloads topic.


Dave Rathbun :us: (BOB member since 2002-06-06)