SSRS Reporting ist keine Einbahnstraße

Die SQL Server Reporting Services können viel mehr, als auf den ersten Blick zu sehen ist. Der folgende Artikel soll dazu anregen über diese "angebliche" Einbahnstraße nachzudenken.

Ein SQL - Mehrere SQLs

Im einfachsten Fall eines Reports hat man nur ein Statement und keine weiteren Besonderheiten.

 

 

Dann werden Möglichkeiten untersucht das Statement dynamischer zu halten und schon ist man bei der Verwendung von Parametern. Diese können mit entsprechenden Plausi-Prüfungen gekoppelt werden und z. B. auch Daten aus einem Kalender beziehen. Die Verwendung im SQL ist dann denkbar einfach:

 

 

Diesen Fall hat sicher jeder schon einmal verwendet und einige haben vielleicht auch diese Möglichkeit schon weiter ausgelotet. Man definiert mehrere Statements, wobei auch

  1. weitere Variablen deklariert
  2. und gefüllt werden können
  3. Die Verwendung der Variablen und weiterer Funktionen erfolgt dann im abschliessenden Select, welches die Daten für den Report ermittelt.

 

 

Hierbei ist zu beachten, dass bei dieser Sammlung von Statements nur ein Resultset zurückgegeben wird, damit auch die Feldliste korrekt ermittelt und weiterverarbeitet werden kann.

Mit Insert

Im nächsten Schritt kann man auch weitere Möglichkeiten ausschöpfen und z. B. temporäre Tabellen oder Tabellenvariablen deklarieren und diese per Insert füllen, aktualisieren, löschen usw. bevor am Ende ein Select die Daten für den Report aufbereitet:

 

 

Was hindert uns eigentlich daran in einem Report mit Insert/Update/Delete auch auf normale (persistente) Tabellen zuzugreifen? Auch "Begin-Try/End-Try" Blöcke sind möglich oder ein "Use database" um den Kontext zu wechseln.

Prozeduraufruf

Wenn man diese Möglichkeiten intensiv anwendet, stellt man schnell fest, dass der Report vielleicht nicht der geeignete Ort ist, um größere Programmierungen vorzunehmen. Verwenden wir also besser eine Prozedur:

 

 

An dieser Stelle können wir auch Variablen übergeben, die wir als Parameter vom Reportserver erhalten. Zu beachten ist lediglich, dass die Prozedur möglichst nur ein Resultset zurückgibt und dies immer die gleichen Spalten hat. Um es einfach zu halten, kann man auch Fehlermeldungen in dieses Format des Resultsets zwingen und darstellen lassen.
Damit haben wir jetzt so ziemlich alle Freiheiten, die man sich wünscht.

Dieses Vorgehen hat noch einen weiteren Vorteil! Man kann das dem Report zugrundeliegende SQL einfach austauschen, in dem man die Stored Procedure ersetzt, ohne den Report selber erneut bereitstellen zu müssen.

In einem Projekt war es nötig ein Tool auf dem Server aufzurufen, welches dort zentral Daten verwaltet und für die Anwender auch ohne Serverzugriff aufrufbar sein sollte. Glücklicherweise verfügte das Tool über eine Kommandozeilenversion, die man aufrufen kann. Packen wir also auch diesen Aufruf in die Prozedur, leiten das Ergebnis in eine Tabelle um:

CREATE TABLE #cmdshell (Zeilennr        integer IDENTITY(1,1),
InfoZeile VARCHAR(255) NULL);

INSERT INTO #cmdshell (InfoZeile)
EXEC @Ergebnis = MASTER..xp_cmdshell @Befehl;

Das Ergebnis wird dann noch etwas aufbereitet und per Select dem Report zur Verfügung gestellt.
Die Sache hat nur einen Haken: Nicht jeder User sollte in der Lage sein über SQL die Command-Shell auf dem Server aufzurufen.
Und auch wenn man dieses Problem umgehen kann, muss man sich hier vor einem Angriff per SQL-Injection schützen. Also niemals Parameter ungeprüft in irgendwelche Statements einfügen und diese dann ausführen!

Ausführen unter anderer Windows-Anmeldeinformation

Was machen wir nun also mit dem Report, den ein einfacher Anwender nicht aufrufen darf, da er nicht die notwendigen Berechtigungen für xp_cmdshell hat?
Es gibt natürlich die Möglichkeit die Prozedur unter dem Benutzerkonto des Erstellers laufen zu lassen, was uns schon weitgehende Berechtigungen gibt. Aber für xp_cmdshell gibt es deutlich höhere Hürden. Zur Berechtigungsvergabe in Prozeduren und in Bezug auf xp_cmdshell gibt es Anleitungen von Tibor Karaszi und Erland Sommarskog. Aber in diesem Falle sollten keine weiteren Berechtigungen vergeben werden und deshalb wurde die Möglichkeit ausgenutzt einen Report unter einem anderen Benutzerkonto ausführen zu lassen.

 

 

In der Datenquelle verwende ich übrigens einen Alias-Namen, um den Report transportabler zu halten.
Bei der Eingabe der Anmeldeinformation wird das aktuelle Kennwort geprüft, falls dies nicht korrekt ist, erscheint die entsprechende Fehlermeldung. Das Häkchen bei der Verwendung als Windows-Anmeldeinformation stellt sicher, dass der Aufruf der Prozedur in dem Report unter dem dort angegebenen Windows-Konto erfolgt. Ist dies ein entsprechend privilegiertes Konto können auch alle Anwender, die Zugriff auf den Report haben über die Prozedur und xp_cmdshell die Kommandozeilenversion des Tools nutzen.
Einziger Nachteil: Falls das Kennwort dieses Kontos regelmässig abläuft, ist hier Pflegeaufwand notwendig.

Weitere Ideen

Mit den oben beschriebenen Möglichkeiten kann man sich nun verschiedene Szenarien vorstellen, die ich im folgenden nur kurz anreißen möchte.

Daten verwalten Ein Report stellt über diverse Parameter die Felder einer Tabelle zur Verfügung und ein weiterer Parameter entscheidet über die Funktionalität (Insert, Update, Delete).
Dateien importieren Ein Report nimmt in einem Parameter den Namen einer CSV-Datei entgegen und startet den Import über SSIS.
Beliebte Parameter abspeichern Ein Report hat viele Parameter und besonders geeignete Kombinationen hiervon sollen sollen abgespeichert oder abgerufen werden. Es werden also zwei zusätzliche Parameter definiert, wobei der eine die Unterscheidung zwischen "IN/OUT/NONE" ermöglicht und der andere Parameter den Namen eines abgespeicherten Parameter-Sets angibt.
Falls die Auswahl "IN" erfolgt, werden die Parameter unter dem Set-Namen zur Wiederverwendung in einer Tabelle abgelegt.
Falls die Auswahl "OUT" erfolgte, werden Parameter aus dem Set abgerufen und für die Selektion verwendet.
Bei der Auswahl "NONE" werden die aktuellen Parameter verwendet.