BusinessObjects Board

Regenerate SQL from VBA

Greetings,

I searched through postings and only came up with one match. However, the match did not have a solution.

Here’s my problem.

I have wrote VB code that takes a value from an input box and runs a case select statement. Based on the value, the ‘Where’ clause in the SQL code is altered to allow different Prompts for the same provider. The code works great however, when I refresh the report a second time, the ‘don’t regenerate’ checkbox is checked. This causes the sql code to keep the previous variable. I can’t seem to find a way in vb to tell the provider to regenerate prior to running the case select statement.

Any help would be great. Please keep in mind I’m a DBA, not a VB coder, so if the code is not perfect, please be kind. Also, it appears that tabs don’t show in this forum window, so some of the documented remarks may run to the end of the SQL Code. Sorry.

Here is the code I’m using.

Sub dpPrompts()

'**************************************************************************
' Code created 10/23/2003, Michael Mayhew, Daughter Business Solutions.   *
' This Module was created for the end-user as a way to choose between     *
' prompts without having all 'OR' sql prompts displayed on-screen.        *
'**************************************************************************

Dim dpMyDataProvider As DataProvider    'Used for BO data provider
Dim iA As Integer                       'Used for loop counter.
Dim iX As Integer                       'Used to hold user input response.
Dim iY As Integer                       'Used to hold the position of the 'Group By' in each SQL Code
Dim strCheckFor As String               'Used to hold the comparison string 'Group By'.
Dim strDPSQL As String                  'Used to hold the DP's SQL Code
Dim strDPSQLbegin As String             'Used to hold SQL Code prior to 'Group By'.
Dim strDPSQLend As String               'Used to hold SQL Code from 'Group By' to end.
Dim strADDSQL As String                 'Used to append the data providers SQL Code.
Dim strDPnewSQL As String               'Used to hold the new SQL Code

'**************************************************************************


'User is prompt for 1=Household_Key 2=LastName or 3=SSN

    iX = InputBox("Enter 1=Household Key or 2=Last Name or 3=SSN?", "Please Make a Selection", 1)

'Sets the dataprovider to dataprovider 1 in document.

    Set dpMyDataProvider = ThisDocument.DataProviders.Item(1)
    
    
    
    'strDPSQL = ""                       'Resets string value.
    strDPSQL = dpMyDataProvider.SQL     'Captures the SQL code in data provider.
    strCheckFor = "Group By"            'Used as the search string against the SQL Code.

    iY = InStr(1, strDPSQL, strCheckFor, vbTextCompare) 'Returns the position of the Group By with in the SQL Code.
    iY = iY - 1                                         'This is the insertion point for adding to the 'Where' clause.

    strDPSQLbegin = Mid(strDPSQL, 1, iY)    'Beginning Code
    strDPSQLend = Mid(strDPSQL, iY + 1)     'Ending Code



'Case Statement based on users response to input box, the system chooses which SQL Code to append to.
    
    Select Case iX

        Case 1
            
            strADDSQL = "AND(ADM.HOUSEHOLD.HOUSEHOLD_KEY )= @variable('Enter Household Key:')"   'This is the text to be added in the 'Where' Clause
            
            'New Code for data provider to process.
            
            strDPnewSQL = strDPSQLbegin & vbCrLf & strADDSQL & vbCrLf & strDPSQLend
           
            ThisDocument.DataProviders.Item(1).SQL = strDPnewSQL
            
        Case 2
             
            strADDSQL = "AND ADM.HOUSEHOLD.INDIVIDUAL1_NAME_LAST_2 LIKE @variable('Enter Last Name')"   'This is the text to be added in the 'Where' Clause
            
            'New Code for data provider to process.
            
            strDPnewSQL = strDPSQLbegin & vbCrLf & strADDSQL & vbCrLf & strDPSQLend
           
            ThisDocument.DataProviders.Item(1).SQL = strDPnewSQL
    
        Case 3
             
            strADDSQL = "AND ADM.HOUSEHOLD.INDIVIDUAL1_SSN_TIN = @variable('Enter SSN?')"   'This is the text to be added in the 'Where' Clause
            
            'New Code for data provider to process.
            
            strDPnewSQL = strDPSQLbegin & vbCrLf & strADDSQL & vbCrLf & strDPSQLend
           
            ThisDocument.DataProviders.Item(1).SQL = strDPnewSQL
    
        Case Else
    
            Dim UserError
            
            UserError = MsgBox("You must enter 1, 2 or 3 as your selection!", vbCritical, "IMPORTANT")
            
        
    End Select

    ThisDocument.DataProviders.Item(2).Refresh

End Sub

MayhewM :us: (BOB member since 2003-10-22)

Michael:

I’ve had much better luck in the past working within the model, and not trying to go around it by adjusting the SQL directly.

What I would suggest, if at all possible, is to consider reworking your code to build your query using the standard options to add or remove conditions. You can still use the dialog box and allow the user to make choices, but instead of adjusting the SQL, adjust the query.

In the long run it will be much more supportable, and it won’t break if new tables and/or joins are added to the query. You’ll let BusObj do what it does best, generate the SQL based on the user selections. 8)

Also, I adjusted your post so that it uses the “code” formatting. It helps make the code stand out more and it recognizes tabs.


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

I agree with Dave. Create three pre-defined conditions, one for each possible prompt. Based on the user input, edit the data provider to use the appropriate prompt as a condition object.

You said you weren’t a VB coder, but looks to me like you have the skills to be able to do this. You might look at the code behind this utility for some ideas. It’s not exactly what you are looking for, but the techniques would be similar.


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

Greetings,

First, let me say thanks for the quick responses.

Second, I have considered the suggestions in both of your replies and have come up with a plan that may work. But I still need some help.

In most cases, users won’t be able to edit the universe and add new conditions without paperwork longer than the IRS format. Therefore, the code I have written would always adjust to added columns within the main data provider. So, my thought is to add a new data provider to the document with the adjusted SQL code string. However, I having difficulties adding a new data provider from VB (Again, a lack of coding skills in VB).

I tried adding the following code at the end of the case select statement.

Dim dpNewDataProvider As DataProvider
Set dpNewDataProvider = ThisDocument.DataProviders.AddDPVBA(“Report Results”)

dpNewDataProvider.SQL = strDPnewSQL
dpNewDataProvider.Refresh

Suggestions are welcome.
Thanks!


MayhewM :us: (BOB member since 2003-10-22)

I would not attempt to create pre-defined conditions… I would use the class / object / operator / operand objects.

Does your query have any conditions to start with? I don’t mean a “where” clause, I assume that it at least has that due to joins between tables. But the query panel itself, does it have any conditions when this process starts?


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

Yes, there is one predefined condition. A filter in the universe called Month_Key. But I’m thinking, all I have to do is create a new data provider to accept the amended SQL Code. Then I won’t have to re-invent the wheel. This should be fairly simple and if I was a vb coder I wouldn’t have this problem :? . I guess I’ll just have to play with it for a little bit. Thanks for the help.


MayhewM :us: (BOB member since 2003-10-22)

Michael, I think you are confusing two different types of data providers.

A VBA data provider … like you were trying to create with .AddDPVBA(“Report Results”) … doesn’t have SQL. A VBA data provider gives you a mechanism to essentially hand create (using VBA) the rows and columns of a data provider. Your code failed because the parameter needs to be the VBA procedure name that creates the data provider, not simply the desired name of the data provider. Check the help and SDK documentation for the proper syntax, if you indeed want to somehow use a VBA data provider.

A universe-based data provider (aka, query technique) has SQL, and yes there is a way to override that SQL, as you’ve already found. Generally speaking though, that is not considered best practice … defeats most of the benefits of using a universe in the first place.

You stated that you’d prefer not to add pre-defined conditions to the universe. That’s fine, we can use Dave’s suggestion just as easily. Assume your three alternative objects are named Obj1, Obj2, and Obj3 and are all in the class called ClassA:

Sub dpPrompts()

    Dim DataProv As DataProvider
    Dim Cond As Condition
    Dim Conds As Conditions
    Dim i As Byte, j As Long

    'get user choice
    i = InputBox("Enter 1=Obj1 or 2=Obj2 or 3=Obj3", "Please Make a Selection", 1)
    If i < 1 Or i > 3 Then
        MsgBox "You must enter 1, 2 or 3 as your selection!", vbCritical, "IMPORTANT"
        Exit Sub
    End If

    Set DataProv = ThisDocument.DataProviders(1)
    DataProv.Load
    Set Conds = DataProv.Queries(1).Conditions
    
    'remove any existing conditions
    If Conds.Count > 0 Then
        For j = Conds.Count To 1 Step -1
            Set Cond = Conds(j)
            If Cond.Class = "ClassA" Then
                Select Case Cond.Object
                    Case "Obj1", "Obj2", "Obj3"
                        Call Conds.Remove(j)
                End Select
            End If
        Next j
    End If
    
    'add the needed condition
    Select Case i
        Case 1
            Call Conds.Add("ClassA", "Obj1", "Equal To", "Enter Obj1 Value", "Prompt")
        Case 2
            Call Conds.Add("ClassA", "Obj2", "Equal To", "Enter Obj2 Value", "Prompt")
        Case 3
            Call Conds.Add("ClassA", "Obj3", "Equal To", "Enter Obj3 Value", "Prompt")
    End Select

    DataProv.Unload
    Application.Interactive = False
    DataProv.Refresh
    Application.Interactive = True

End Sub

Now when the query is refreshed, BusObj will write the SQL for you.


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

Thanks for the help. I will give this a try on Monday and let you know how it works out. I will also try changing the original dataproviders to free-hand sql and then try using my original code I put together, just to see which one works better.

Thanks again!


MayhewM :us: (BOB member since 2003-10-22)

… except that you can’t manipulate free hand SQL via VBA. :wink: We discusssed that just a bit ago in this topic.


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

Greetings again.

Well, I hate to differ with you, but I was able to manipulate the Free-hand SQL in BO6.1 using my code from the original example. However, I would have to tell the code to drop off the new portion of the SQL statement after a refresh was complete.

I am still experimenting with the conditions code. Our client’s unix server is down, so I probably won’t get a chance to test it today.

I will say, I am leaning more toward the conditions coding only because it’s a little cleaner. Basically, I think the results are the same though.

Thanks again for everyone’s input!


MayhewM :us: (BOB member since 2003-10-22)

Me again.

Dwayne, I tried:

Set Conds = DataProv.Conditions

but .Conditions is not recognized in the vb editor.

Any other ideas?

Thanks,
Michaael


MayhewM :us: (BOB member since 2003-10-22)

My mistake. It should be Set Conds = DataProv.Queries(1).Conditions (assuming you have only one query tab). I have also changed it in the original message, in case others happen upon the code later on.


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

Hi again Dwayne,

The code still needs a little refining, but is working.

Where you have “Equal”, the code should read “Equal To”.

Also, I’m having difficulty deleting existing conditions in the loop. I can get it to recognize the class of the object, but condion.delete is not recognized. I tried condition.object.delete, but no luck either.

If I can get it to delete the existing condition, the code should be working properly.

Thoughts are welcome… Thanks again for your time.


MayhewM :us: (BOB member since 2003-10-22)

This may be a stupid question, but why not have two prompts, one for the choice (“Enter 1=Household Key or 2=Last Name or 3=SSN?”) and one for the value. Then use a DECODE or CASE statement to restrict the data accordingly.

DECODE(@prompt('Enter 1=Household Key or 2=Last Name or 3=SSN?', 'N', {1, 2, 3},,),
       1, ADM.HOUSEHOLD.HOUSEHOLD_KEY,
       2, ADM.HOUSEHOLD.INDIVIDUAL1_NAME_LAST_2,
       3, ADM.HOUSEHOLD.INDIVIDUAL1_SSN_TIN)
    LIKE @prompt('Enter Household Key/Last Name/SSN)

Dennis W. Disney :us: (BOB member since 2003-09-17)

I should point out that my above code will blow your index and reduce performance, but depending on the size of the dimension table, that may not be an issue.


Dennis W. Disney :us: (BOB member since 2003-09-17)

OK Michael, I think I have it right this time. I knew I had done this before, but I don’t have that code code any longer, so I had written my original stuff from memory without testing it … again my apologies.

I have now corrected it (I think). Take a look.


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

The thing to remember about deleting conditions is you have to start with the last one and move up. If you want to delete all conditions, that is. Say there is a query with 5 conditions, and you create a loop to delete them all. After deleting one, there are 4 left. After 2, 3 left. After 3, there are only two left, but your loop index is at 3.

Here’s some sample code that I use to clear all conditions from a query:

Sub ClearConditions(ByRef boDP As busobj.DataProvider, bolIsLoaded As Boolean)

'   Parameters
'       boDP        a BusinessObjects data provider
'
'   Parameters passed by Reference to save on memory. No changes are done during this subroutine.
'
'   This procedure is responsible for clearing out any pre-existing conditions on the
'   query. 

    Dim i as Integer, j as Integer, intQryCount as Integer, intConditionCount As Integer
    Dim boQuery As busobj.Query
    
    On Error GoTo ErrorHandler
    
    If Not (bolIsLoaded) Then
        boDP.Load
    End If
    
    intQryCount = boDP.Queries.Count
    For i = 1 To intQryCount
    
        Set boQuery = boDP.Queries.Item(i)
        
        ' Conditions are removed in reverse order. This is required to avoid errors.
        ' If there are 5 conditions and you remove condition 1, then there are only
        ' four conditions. Removing condition 5 will generate an error. By removing
        ' the conditions in reverse order, we guarantee that the condition referenced
        ' by the loop index will exist.
        
        intConditionCount = boQuery.Conditions.Count
        For j = intConditionCount To 1 Step -1
            boQuery.Conditions.Remove (j)
        Next j
        
    Next i
    
    If Not (bolIsLoaded) Then
        boDP.Unload
    End If
    
    On Error Resume Next
    Exit Sub
    
ErrorHandler:
    Call errorProcedure("ClearConditions")
End Sub

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

Again, I want to thank everyone for their time and if I can ever be of help, don’t hesitate to ask.

Dennis, I am dealing with about 300 million rows. I don’t think I want to lose any speed. Sorry, but thanks for the input, I’m sure it will find a use in the future.

Dwayne, I’ll give the new code a try and let you know.

Dave, In my instance, I will not be deleteing all conditions. Only those created from the previous refresh of the query. So, between Dwayne’s code and yours, I’m sure I’ll be able to work it out.

Thanks again, I will advise.


MayhewM :us: (BOB member since 2003-10-22)

Believe it or not, I have Dwayne’s code working great. Thanks much!.

However, I have one little glitch in the code due to the new feature in BO6.1 for re-using data sets.

I’m not sure the feature works like they intended to because when I use it to pull back household hold keys from the new data provider and tell the second data provider to refresh, it prompts me for the same prompts over again and re-runs the first query. If I manually put a check in the box “not refreshable” from the data manager, the second refresh works fine.

Here’s my problem. When I try to set the isrefreshable function in vb I get one of two error messages.

Error 1 = Error Object not set
or
Error 2 = No Rights, cannot refresh dataprovider.

dp1.refresh
dp1.isrefreshable = false
dp2.refesh
dp1.isrefreshable = true.

Any ideas???
Thanks!!!


MayhewM :us: (BOB member since 2003-10-22)

Only the owner of the document can set those attributes. When you open the data provider (click the “Cube” button on the toolbar) and look at the Definition tab, can you set the Editable or Refreshable check boxes? If not, you need to create a separate version of the document that you own, and they you should be able to adjust those properties.


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