THE SQL Server Blog Spot on the Web

Welcome to - The SQL Server blog spot on the web Sign in | |
in Search

Erin Welker

SQL Server and Business Intelligence

Automating UBO

I was preparing for my presentation at PASS and wanted to show some code for automating usage-based optimization.  This is the process that allows for creating aggregation designs based on past query activity.  I was surprised to find that there were no code examples posted on the web on how to do this, at least none that I could find.  BIDSHelper and AggManager have similar functionality but, upon examining the code, they both use a more manual approach of examining each Dataset column in the query log and creating an aggregation when it encounters a "1" in the bit string.  I believe they did so due to the inferiority of the UBO algorithm in SQL Server 2005.  That being said, using the out-of-the-box UBO algorithm leveraged here is only recommended for use with SQL Server 2008.

I felt sure that this functionality had to be available in AMO (Analysis Management Objects) because it existed in its predecessor, DSO.  It turns out that there is supporting documentation in Books Online, but it took some help from some of the folks at Microsoft to find it.  Note to self, when looking for detailed documentation of functionality in AMO, look under XMLA.  This kind of makes sense - I just would have expected it to be replicated in the AMO documentation.  Maybe it just takes a developer to know how to find it.  XMLA can also be used as a means of automating UBO but I find the AMO method to be a little more elegant.

The methods used for leveraging query logs when designing aggregations are the same as those used in generic aggregation design.  The only difference is that a collection of queries is passed in addition to the other parameters.  There are two things to note, however: 

First, the Dataset column that is read from the query log must be preceded by a weighting factor, usually the number of times the query appeared in the log. 

Second, the queries string collection must only be passed the first time the method is executed.  If you pass this parameter in on subsequent calls you will get an error.  The DesignAggregations method is executed in a loop that is terminated when the size and/or optimization goals are met.  I'm not sure why it couldn't just ignore the queries parameter in subsequent calls, but it doesn't, so you need to code accordingly.

Both of these nuances are documented in Books Online.

Here's an AMO code snippet for adding an aggregation design based on query history.  Note that development is not among my primary skill sets, so you'll probably want to clean it up - but it works.  For the sake of the session demo, I hard-coded a measure group and created a brand new aggregation design.  Best practices indicate that you should add these new aggregations to the existing design so that you won't throw away aggregations that were leveraged by previously well-performing queries. In a real-world scenario, you'd also want to filter the query log so that you only design aggregations for queries that had a long Duration:

Public Sub Main()

   ' Declarations

   ' Get Server and Database name from DTS connection object 
   Dim oSSASConnection As ConnectionManager = Dts.Connections("Analysis Services")
   Dim oSQLConnection As ConnectionManager = Dts.Connections("SQL Server")

   Dim sSSASServer As String = CStr(oSSASConnection.Properties("ServerName").GetValue(oSSASConnection))
   Dim oSSASServer As New Microsoft.AnalysisServices.Server

   ' SQL Connection
   Dim strSQLQuery As String
   Dim strConnection As String
   Dim sqlCn As SqlConnection
   Dim oServerProperty As ServerProperty
   Dim sqlDataAdapter1 As SqlDataAdapter
   Dim dsQueryLog As DataSet
   Dim dvQueryLog As DataView
   Dim dRow As DataRow

   ' Aggregation variables
   Dim Queries As New StringCollection
   Dim strAggPrefix As String
   Dim aggName As String
   Dim aggDesign As AggregationDesign
   Dim optimization As Double = 0
   Dim storage As Double = 0
   Dim aggCount As Long = 0
   Dim finished As Boolean = False
   Dim firsttime As Boolean = True

   ' Measure group variables
   Dim DatabaseName As String
   Dim CubeName As String
   Dim oMeasureGroup As MeasureGroup
   Dim MeasureGroupID As String
   Dim oPartition As Partition

   ' Initialize
   DatabaseName = "Adventure Works DW 2008"
   CubeName = "Adventure Works"
   MeasureGroupID = "Fact Sales Summary"
   strAggPrefix = "PASS2009_"



      ' Initialize connections
      oServerProperty = oSSASServer.ServerProperties("Log\QueryLog\QueryLogConnectionString")
      strConnection = oServerProperty.Value.Substring(oServerProperty.Value.IndexOf(";") + 1)

      sqlCn = New SqlConnection(strConnection)

      oMeasureGroup = oSSASServer.Databases(DatabaseName).Cubes(CubeName).MeasureGroups(MeasureGroupID)

      ' This would be a good place to update the EstimatedRows in the measure group and partitions
      ' Set oMeasureGroup.EstimatedRows = to the count of rows in the source fact table

      ' Get select queries from the query log
      strSQLQuery = "SELECT dataset, COUNT(*) FROM OLAPQueryLog WHERE MSOLAP_Database = '" & DatabaseName & "' " & _
          " AND MSOLAP_ObjectPath = 'ERIN-PC\SQL2008." & DatabaseName & "." & CubeName & "." & oMeasureGroup.ID & _
          "' GROUP BY dataset"
      sqlDataAdapter1 = New SqlDataAdapter(strSQLQuery, sqlCn)
      dsQueryLog = New DataSet
      sqlDataAdapter1.Fill(dsQueryLog, strSQLQuery)
      dvQueryLog = dsQueryLog.Tables(strSQLQuery).DefaultView

      ' Populate the Queries string collection with the distinct queries from the query log
      For Each dRow In dvQueryLog.Table.Rows
         Queries.Add(dRow(1).ToString & "," & dRow(0).ToString)

      ' Add a new design to the Fact Sales Summary measure group and design aggregations based on the passed list of queries
      aggName = strAggPrefix & "_" & oMeasureGroup.Name

      aggDesign = oMeasureGroup.AggregationDesigns.Add
      aggDesign.Name = aggName

      Do While ((Not finished) And (optimization < 100))
         If firsttime Then
            aggDesign.DesignAggregations(optimization, storage, aggCount, finished, Queries)
            firsttime = False
            aggDesign.DesignAggregations(optimization, storage, aggCount, finished)
         End If


      ' Assign the new aggregation design to all partitions in the measure group
      For Each oPartition In oMeasureGroup.Partitions
         oPartition.AggregationDesignID = aggDesign.ID

      ' Process the indexes to build the new aggregations
      ' oMeasureGroup.Process(Microsoft.AnalysisServices.ProcessType.ProcessIndexes)

      Dts.TaskResult = ScriptResults.Success

   Catch ex As Exception
      Dts.Events.FireError(0, "Design aggregations failed - ", ex.Message, "", 0)
      Dts.TaskResult = ScriptResults.Failure
   End Try

   If oSSASServer.Connected Then
   End If

End Sub
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

Published Sunday, November 15, 2009 11:14 AM by ErinW


No Comments
New Comments to this post are disabled
Privacy Statement