Reporting Services (RSS)

MS SQL Server 2000 Reporting Services-related posts

Quick Tip: Retrieving Report Definitions from the Catalog Table in the ReportServer Database

The Reporting Services Catalog table in the ReportServer database contains the RDL XML in the Content column, serialised to binary format.

I had an issue recently where I needed to retrieve the RDL XML from the Content column from a backup of the ReportServer database using SQL Server 2005. Here's the code that did the job:

SELECT  [Name], 
        CAST(CAST([Content] AS VARBINARY(MAX)) AS XML) AS reportXML 
FROM    ReportServer_Backup.dbo.[Catalog] WITH (NOLOCK) 
WHERE   --get only reports, not folders or other resources 
        [Type] = 2 

I would not recommend running this against the entire table. I've used the NOLOCK hint to avoid locking up the table, and also screened our non-reports by adding Type = 2 to the where clause.

I originally found this great little snippet by at the SQL Server and XML blog. Thanks Jacob! It came in so useful that I thought I'd write it down for later.

Tags: sql server, reporting services, database, xml

Reporting Services Matrix Techniques

I haven't posted anything on Reporting Services recently.

So, I have two links for the infamous matrix control:

Because I feel I need to compensate :-)

Tags: sql server, database, reporting services, matrix

Reporting Services Heatmap

In Reporting Services, the syntax for changing the text color of a table cell, say to red if the value is negative, is to set the "Color" property to this expression:

=IIf(Me.Value < 0, "Red", "Black")

Another useful formatting tool is changing the background color of a cell depending on the cell's value, perhaps to achieve a "heatmap" effect. I find it's far better to do this using code than the macro-type language I used above.

Setting the background color in code can be achived by putting an expression like the following into the "BackgroundColor" property, replacing the section in square brackets with the same expression in the cell's "Value" property:

=Code.GetHeatmapColor([cell value or calculation], "White")

The second parameter is for a default color. You may or may not want to use it.

My sample code for "GetHeatmapColor" will be a simple SELECT CASE. First you need to define how the "heatmap" will work. For this example, "0" (or below) is the worst possible value and will appear dark red. "10" (or above) is the best possible value and will have no color at all.

Here's the code for "GetHeatmapColor", which accepts a double:

' Returns a background color from dark red to light pink (hey, that's what you get 
' when you mix red and white) based on how far off target (in this case, 10) the
' passed value is.
Public Function GetHeatmapColor( _
        ByVal dblActual As Double, _
        ByVal strNeutralColor As String) As String
   
    ' measure how far the result is from the target (10), and grade the colors accordingly
    ' in blocks of 2 (you get the idea, anyway)
    Select Case CInt(dblActual - 10)
        Case < -10
            ' below zero (way off our target of 10)...return worst color
            Return "#FF8282"
        Case < -8
            ' off target - either 0 or 1
            Return "#FF9191"
        Case < -6
            Return "#FFA0A0"
        Case < -4
            ' Noticeably off target
            Return "#FFAFAF"
        Case < -2
            Return "#FFBDBD"
        Case < 0
            ' mildly off target - either 8 or 9
            Return "#FFCACA"
        Case Else
            ' on or above target (10) - return neutral color
            Return strNeutralColor
    End Select
   
End Function

And lastly here's a screenshot of a simple report that uses this exact code, the first column is ascending and the second is mixed up. Note how easy it is to pick the worst and best values in the second column because of the "heatmap" colors:

Click here if the picture does not display

I generated the colors by finding the darkest acceptable color (not quite red) that black text was still readable on top of, then creating a gradient using Paint.NET fading from this color to white, then finally using a "pixelate" effect in Paint.NET to remove the gradient smoothness and get definite colors. I'm sure you can think of better ways to do this!

I hope this code is able to help someone. As always, it worked on my machine; I make no guarantees for yours!

Tags: reporting services, heatmap, color, visualisation

Leaving out Parameters on SQL Reporting Services

Reporting Services puts parameter selection before the top of the report, with (at most) 2 parameters on a row. If your report has a lot of parameters (my example below has 5), the parameter section can take up a lot of room:

As an aside, having multiple parameters is better than having multiple reports which differ only by a WHERE clause in the database. Parameters are your friend - it's just the parameter display (which is not configurable at all) that I'm trying to look at here.

So taking up a lot of screen real estate with parameters is a bad thing. If the report already has default values set for parameters (a must), I reckon that if a parameter only has a few options, and the default is going to be used 80% of the time, one solution is to mark the parameter as "Hidden" and then provide a link in the report which will toggle it. Here's my example of the same report with only 3 parameters:

In my example with only 3 parameters, more of the space is dedicated to the report and less to the parameters. I've shown what the current current parameters are so when the report is printed or exported, the settings used to run it are visible.

My "click to change" text is just a label that has the "Action" property set to run the same report with the changed parameter (remember, I used parameters that only have two values). An additional task is to set the text from an expression, but I reckon getting the text into this plain english format is worth it.

Side another aside, I thought I'd blog this because I've been listening to Adam Cogan's session from SQL Code Camp NZ and checking out the SSW Rules to Better SQL Reporting Services 2005, both very helpful. Just thought I'd add to the collected wisdom :-)

Tags: reporting services, parameters, design, default

Keeping Track of Report Server Custom Security Settings

Reporting Services allows many nested levels of folders, just like Windows, and when you apply security settings to a folder the settings flow down to child items - which can be other folders, reports, and items like Excel or Word files. However when you change security settings on a child item you disengage it from the parent, so that any changes from then on to the parent won't affect the child (unlike Windows security, where you can "inherit" the parent settings and add child settings).

In my mind this makes security settings in Reporting Services hard to keep track of. Items either inherit the parent security settings, or have their own. This creates a problem if you want to change security settings on a global level, as if you make a change to a parent, any children that have seperate or custom security settings will not reflect the changes. Maintaining lots of different security settings on folders and reports could also become a maintenance problem very quickly.

However, the security settings in Reporting Services are very thorough. If you do not explicitly grant access to a report or folder to a user, that user will not even be able to see a link to the report. Manually browsing or typing in the URL to the report or folder won't get them there either - it's like the item does't exist! This can be helpful in some circumstances.

So I came up with the following 2 queries (and went on to make them into a Reporting Services report) that will show the items that do not inherit parent security and allow you to keep track of them, for the times when you absolutely, positively, must use Report Server security settings.

The first query returns the items on the Report Server that have custom security settings (to run this, you'll need to have access to the ReportServer database):

Disclaimer: use at your own risk. The following queries work for me -  I don't make any guarantees for anyone else.

USE ReportServer
GO
-- *********************************************************
-- Query 1: list all items that have security changed from parent item
-- *********************************************************
SELECT  C.ItemID,   --primary key and identifier, UNIQUEIDENTIFIER, NOT NULL
        C.Path,     --full path from root, which can be used in an expression like
                    --"http:///Reports/Pages/Folder.aspx?ItemPath=X" (X needs to be URL-encoded), NVARCHAR(425), NOT NULL
        C.[Name],   --name of item, NVARCHAR(425), NOT NULL
        C.ParentID, --ID of parent (links back to ItemID), UNIQUEIDENTIFIER, can be NULL
        C.Type,     --type of item in the "Catalog" table - DataSource = 5, Folder = 1, LinkedReport = 4, Report = 2, Resource = 3, Unknown = 0                     --else (e.g. PDF, DOC), INT, NOT NULL
        C.Hidden    --1 for hidden, 0 for visible, BIT, can be NULL
FROM    --get data from the Catalog table
        dbo.[Catalog] C WITH (NOLOCK)
WHERE   --don't get user folders ("My Reports")
        (NOT (C.Path LIKE N'/Users Folders%')) AND
        --only get changed items: Catalog.PolicyRoot setting of 1 means that the item has
        --specific permissions set, 0 means it inherits the parent settings
        (C.PolicyRoot = CONVERT(BIT, 1)) AND
        --don't get the root item (top-level folder)
        (C.ParentID IS NOT NULL)

I would not recommend querying the live ReportServer database, but rather copying the data out to another database on some sort of schedule for querying. You never know what Reporting Services is doing under the hood with it's own database.

That said, the query above returns data from the "Catalog" table for items (folders, reports, data sources, etc.) that have custom security settings. The query below will return the users that have access, given an ItemId:

DECLARE @ItemId UNIQUEIDENTIFIER --item to search for
SET @ItemId = ''
-- *********************************************************
--Query 2: list users who have access to a specific item
-- *********************************************************
SELECT DISTINCT
        C.Path, U.UserName
FROM    dbo.[Catalog] C WITH (NOLOCK) INNER JOIN
            dbo.Policies P ON C.PolicyID = P.PolicyID INNER JOIN
            dbo.PolicyUserRole PUR ON P.PolicyID = PUR.PolicyID INNER JOIN
            dbo.Users U ON PUR.UserID = U.UserID
WHERE   --get the passed item only
        (C.ItemID = @ItemId)

Used together, these two queries can help keeping track of which items on a Report Server have custom security settings. Enjoy!

UPDATE: Updated Item Types in 1st query thanks to Lutz Roeder's .NET Reflector

"Microsoft Access: Upgrading and Migrating to SQL Server and .NET" - Melbourne, December 2004

A brief bit of history - we switched our internal reporting over to MS Reporting Services after seeing the product in action (albeit in beta form) December last year. Gradually we're in the process of moving Access databases, Excel reports (on file shares, e-mail and Exchange Public Folders), rich-text e-mails and the odd VB front-end over to SQL Server back-ends, Reporting Services for reports, and Windows Forms (VB.NET) for the front-end. I personally have had experience moving over 3 or 4 such projects, and my two co-workers have not had any experience (some of their legacy systems are still in Access 97). I reckon there's enough migrations to keep us busy all next year!

Into the above context steps a brand new Microsoft training course "Microsoft Access: Upgrading and Migrating to SQL Server and .NET" which covers exactly what I've described above. We (me and the 2 co-workers) attended the Melbourne event on December 13th and 14th. The course was run by Adam Cogan from Superior Software for Windows which for me was a big selling point as I'd heard Adam speak before (and he was good).

I enjoyed the training, and here's why (in no particular order):

  • The facilities at Cliftons were fantastic. P4 3.2/1GB RAM/17“ LCD Win XP Pro machines. Two people per desk, and in addition each desk had a monitor that displayed the presenter's computer. Classroom was 80% full, around 35 people.
  • Adam was an enjoyable and knowledgeable presenter. Just hearing him describe tips and tricks was worth the price of admission for me, but then again I had already been through the process he described a number of times. And he wasn't shy about sharing his knowledge and useful tools either (which unfortunately left very little time for the hands-on labs).
  • The material covered was pretty much spot-on in terms of the need it addressed. The first day a bit more time was spent on tools that Adam's company make to ease the migration process, but in my mind it was very relevant to the task at hand. Adam even branched out to talk about Extreme Programming practices, e-mail etiquette and job quoting & estimation, all within the context of an upgrade project.
  • Adam was realistic in his praise/criticism about Microsoft. I didn't feel like I was at an indoctrination session. In fact, the SSW website lists a whole lot of possible improvements with MS products.
  • Adam has a good sense of humour, which seemed to grease the wheels of the presentation.

There were some minor issues:

  • There was no printed courseware/reference material. The labs had handouts which referred to documents available on everyone's PC. Call me old fashioned, but I want to walk away from a training session with something to show and something to look back on when I start to forget things.
  • The labs were fairly short. As mentioned, in one way this was fine because Adam got to share and demo a bit more, but I'm a hands-on guy so doing things means knowledge sinks in.
  • We talked about Extreme Programming, but didn't get the chance to practice it (this is probably just a personal gripe as I doubt if any other attendees would have wanted to pair program with me!) I felt that the setting and “project” was prime material for XP practices.

Here's some of the key points, for someone thinking of following the upgrade path:

  • Upgrade in sections - back-end to SQL Server, reports to Reporting Services, front-end to Windows Forms. These are discrete and deliverable sections of the overall upgrade.
  • Windows Forms offer a richer experience for your front-end, especially in a controlled environment (e.g. you assume that everyone has the .NET Framework).
  • Separate your front-end development into a UI layer, a business rules/logic layer, a data-access layer and a separate project for strongly-typed datasets.
  • NUnit is good.

There were many other points, but if I told you them all I'd be giving it away...attend the training!

Overall a great event, one of the best trainings I have ever attended. Well done, Adam and team.

Reporting Services rs.exe Utility

This week I've had the opportunity to write a batch file which is run from the rs.exe command-line tool to administer Reporting Services.

As far as I can tell, there's 3 ways you can create folders or deploy reports or change settings in Reporting Services: from within Visual Studio.NET; using the Report Manager web interface; and using rs.exe (which is itself a wrapper for Reporting Services web service calls). I use the first way to do simple things like deploy reports until our virtual folder structure got deeply nested, where I switched to the second way - design and build the report in Visual Studio.NET, then go to my web browser and upload the file.

I didn't get to use rs.exe until recently because I really had no need. This command-line utility takes a script file which is written in VB.NET (not VBScript) and performs actions on a Reporting Services server like uploading reports, creating folders, creating data sources, setting properties, etc. Within a short time I had adapted some sample scripts and written a VB.NET file with the special “rss“ extension in Notepad, and then set up the command line necessary to run it in a command prompt window.

After going through the edit, save, run command line cycle (which compiles the script into an exe in your temp folder, and then runs it) a few times I thought to myself that it would be far better to compose and run the file from the Visual Studio.NET UI to get full Intellisense, syntax coloring, etc. On opening the rss file with VS.NET I noticed that the file was not syntax colored and did not have Intellisense - which led me down the side track of trying to add that file extension so it could be handled like a “*.vb” file by VS.NET (I didn't figure out how to do this, though).

After finishing my task, I can say that rs.exe is definitely powerful but had some strange quirks that I hope get ironed out in future releases. The summary of my experiences is:

  • VB.NET is very powerful, and all the namespaces are available in the “rss“ file (whoever came up with that extension? It is impossible to search the web with the term “rss“), but, I feel I should be able to edit the file with VS.NET to get syntax coloring and Intellisense (I know, I've laboured the point).
  • There is no built-in method on the in-built “rs“ object for deleting an existing virtual folder in Reporting Services (without resorting to SOAP calls, I think). This was a bit of a problem as if a folder I was trying to add exists, I wanted to delete it and add it again, but eventually I settled on raising an error and quitting the script.
  • If the script adds a folder, then adds a data source to the folder, then adds a report to the folder, I couldn't wrap it in a batch (using either “-b” as a command-line option or CreateBatch/CancelBatch within the script) as when I tried to do anything with the newly-created folder, I got an error “Folder doesn't exist”. I thought the batch would handle that...
  • There's not a lot of sample scripts out there for using rs.exe, yet.
  • I couldn't figure out how to get the name of the running script or the folder it's in, because rs.exe compiles the running script as a temporary exe in your temp folder. So, all Windows folders containing “rdl“ files for uploading had to be either hardcoded or passed in the command line.
  • I couldn't work out how to exit the script with an error - like, a DOS error code - so, when an error came up that should cause the script to fail in it's entirety, I used the VB.NET keyword “End“ (which I thought was a bit nasty!)

Sample Financial Reports for Reporting Services

Mike Gunderloy points to “SQL Server 2000 Report Pack for Financial Reporting” sample reports from Microsoft.

I found that the installer crashed on my W2K Server box with a cryptic error message, but ran OK on my XP Pro desktop (where are the error logs for these things?)

I do have one gripe, while trying not to look the gift horse straight down the throat, that why is an installer required to deliver the RDL and MDB files? I reckon that maybe a copy of the database with instructions to attach it and the Visual Studio solution file and report files would be enough, because now there's an extra entry in my “Add or Remove Programs“, that is not a program (on the plus side, the installer does not put a link in my “All Programs“ start menu folder, like some of the other samples I've downloaded in the past).

However, I can see the advantage to having an installer for access to powerful scripting and system requirements stuff.

Maybe my next task should be to find a way to examine and extract the contents of MSI packages...or maybe to shut up and be grateful for the sample reports...grumble grumble grumble...

SQL Health and History Tool, plus Reporting Services Reports to go with them

Darrell Norton points out a tool to monitor SQL Server 2000, plus Reporting Services reports to view the data collected. I was aware of the Health and History tool before, but having these reports makes it more attractive.

It's good to see reports designed for Reporting Services to cope with “enterprise” needs - I was fossicking around the Superior Software for Windows site the other day and noticed that they'd developed Reporting Services reports for Exchange. Also, the Reporting Services CD comes with reports to monitor usage of Reporting Services itself.

Pivot Tables/Horizontal Tables With Reporting Services

Chris Hays shows step-by-step how to do “horizonal tables” (fixed rows, variable columns) with Reporting Services. I'm bookmarking this to have a look at later.

Chris uses the matrix which is an interesting Reporting Services control. I've used this in the past to take advantage of the multi-level drill-down which I reckon is slightly easier to use than a table control. The subtotals are easier than tables too, but there's a trade-off because with the table you have very tight control over what gets displayed, and how, compared to the matrix.

URLScan, FlexWiki and Reporting Services

Here's hoping that someone will find this information useful when working with the IIS Lockdown Tool