SSAS Objekte per AMO verarbeiten und Verlauf per SessionTrace verfolgen
Einen Microsoft Sql Server Analysis Services (SSAS) Cube und dessen Objekte lässt man im produktiven Betrieb natürlich zeitgesteuert per SSIS Package verarbeiten; da will man nicht wirklich bei „zusehen“.
Während der Entwicklungs- & Designphase eines Cube’s lässt man diesen aus BIDS heraus verarbeiten. Die Daten basieren dabei meist auf Views, die die eigentlichen Daten auf eine repräsentative als auch vertretbare Datenmenge beschränken; schließlich verarbeitet man öfter mal den Cube, um das Ergebnis zu kontrollieren und das soll ja nicht jedes Mal einen halben Arbeitstag in Anspruch nehmen. Der Processing Verlauf wird dabei in BIDS (und auch SSMS) in einem TreeView dargestellt und das bietet einem nicht gerade eine gute und informative Darstellung des Verlaufes.
Andererseits sind die Verlaufsinformationen (mir) wichtig, um von den Test- auf die Produktionsdaten hochrechnen zu können.
AMO bietet die Möglichkeit mit einem .NET Programm oder PowerShell programmatisch einzelne Objekte eines Cubes verarbeiten zu können. Über einen Trace könnte man alle Aktivitäten auf dem Analysis Server nach verfolgen und die der eigenen Session herausfiltern; nicht so ganz einfach. Es geht aber auch wesentlich einfacher, indem man das SessionTrace des AMO Server Objektes verwendet, über das man auch die Verarbeitung der Objekte durchführt. Man setzt eine Variable WithEvents, startet den Trace und kann dann alle Ereignisse Event-gesteuert empfangen. In TraceEventArgs erhält man alle Daten, so wie man sie auch aus dem SQL Profiler kennt.
Es gibt aber kleine Probleme mit ein paar Properties von TraceEventArgs, die könnten durch „Ungenauigkeit“ (alias Bug) in AMO verursacht werden und die gilt es zu umschiffen. Das betrifft zum Beispiel das Property IntegerData. Natürlich und verständlicherweise gibt es nicht bei jedem TraceEvent einen Wert für IntegerData, aber dann würde ich davon ausgehen, das entweder Nothing (C# null) oder -1 geliefert wird. Stattdessen erhält man den Laufzeitfehler
System.ArgumentNullException: Der Wert darf nicht NULL sein. Parametername: String
bei System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
bei System.Number.ParseInt64(String value, NumberStyles options, NumberFormatInfo numfmt)
bei Microsoft.AnalysisServices.TraceEventArgs.get_IntegerData()
egal was man macht; eine einfach Prüfung auf IsNothing reicht bereits als Auslöser. Nun könnte man aufwendig auf alle EventClass / EventSubClass Kombinationen filtern, wo zugesichert ein Wert vorkommt. Ich mache es mir einfacher und deswegen kapsele ich die Abfrage des IntegerData, das bei manchen Events den Fortschritt bzw. die Anzahl verarbeiteter Datensätze enthält, in einer eigenen Funktion und fange den Fehler mit Try Catch ab. Betroffen sind von dem Phänomen die Properties CpuTime, Duration, IntegerData, ProgressTotal, Serverty.
Der Rest ist einfach, ich lasse die TraceEventArg-Daten in einer Console ausgeben, der VB.Net Code dazu sieht so aus:
Imports Microsoft.AnalysisServices
Module DimProcess
Private WithEvents mSessionTrace As SessionTrace
Sub Main()
Dim server As New Server
server.Connect("MeinServer\MeineInstanz")
Dim database As Database = server.Databases("Adventure Works Cube")
Dim dimension As Dimension = database.Dimensions("Dim Customer")
mSessionTrace = server.SessionTrace
mSessionTrace.Start()
Console.WriteLine ("{0} Start of processing", Date.Now().ToString("T"))
dimension.Process(ProcessType.ProcessFull)
mSessionTrace.Stop
mSessionTrace = Nothing
Console.WriteLine ("{0} End of processing", Date.Now().ToString("T"))
dimension.Dispose
database.Dispose
server.Disconnect
server.Dispose
Console.WriteLine ("Hit any key")
Console.ReadKey
End Sub
Private Sub mSessionTrace_OnEvent(ByVal sender As Object, ByVal e As Microsoft.AnalysisServices.TraceEventArgs) _
Handles mSessionTrace.OnEvent
Select Case e.EventSubclass
' Uninteressante EventSubClass:
Case TraceEventSubclass.Subscribe, TraceEventSubclass.SqlQuery, _
TraceEventSubclass.DiscoverXmlMetadata, TraceEventSubclass.ExecuteSql
Case Else
If Not String.IsNullOrEmpty(e.TextData) AndAlso not e.TextData.StartsWith("<") then
Console.WriteLine ("{0} {1} {2}", Date.Now().ToString("T"), GetIntegerdata(e), e.TextData)
end if
End Select
End Sub
Private Function GetIntegerdata(ByVal e As TraceEventArgs) As string
Try
Dim result As long = e.IntegerData
Return result.ToString().PadLeft(6)
Catch ex As Exception
Return String.Empty.PadLeft(6)
End Try
End Function
End Module
Zugegeben, mit dem AdventureWork Demo gibt es nicht groß Daten zu verarbeiten, deswegen ist die Ausgabe auch nicht gerade spektakulär: