Click here to monitor SSC

FatherJack

SQL Q+A forum at ask.sqlservercentral.com | Follow fatherjack on Twitter

Where did I hide my TSQL mojo?

Published Monday, January 24, 2011 3:00 AM

A little while ago I wrote a piece about finding database objects that rely on other objects that no longer exist - OK, I have my database ready, now what's missing? . This is linked to that sort of process. Many SQL Server installations are associated in some way with a Reporting Services installation, it's a very logical way to distribute your database contents to system users so they can work effectively.

Databases, in many cases, are very dynamic and have work going on to make them more efficient or to carry out different roles and processes. This means that some objects come and go and if you happen to have a report that was written to consume data from a specific stored procedure and the procedure gets dropped in favour of a better method or even if the name stays but the dataset changes then your report can suddenly start throwing errors instead of making an accountant happy.

Unless you have incredible documentation on your deployed reports and the TSQL they use, whether to reference a stored procedure or a view etc., then you can easily make a breaking change.

One way to avoid an angry accountant is query the Report Server database. Now, the rdl files that you upload to your Report Server are stored in the database as a binary data type so there is some shenanigans to getting in there and seeing what's going on.


First of all lets use a temp table so we don't do anything silly to the live Catalog table
IF OBJECT_ID('tempdb..#ReportContents') >0
   
DROP TABLE
#ReportContents
GO
-- collect required rows
SELECT
   
[PATH]
,
   
NAME
,
   
CONVERT(XML,'')AS xml_content
,
   
CONVERT(VARCHAR(MAX),REPLACE(CONVERT(VARBINARY(MAX),c.Content),''
,
                                 
'')) AS
[Content]
INTO
   
#ReportContents
FROM
   
dbo.[Catalog] as C
WHERE
   
[C].[Type]=
2
   
ANDLEFT(NAME,1) !='{'


-- create XML type content
UPDATE
   
[#ReportContents]
SET
   
[xml_content]=CONVERT(XML,content)

So this leaves us with human-readable report definitions, albeit in an XML format...  
-- prove data is there
SELECT
   
[rc].Name,
    [rc].[xml_content]
,
   
[rc].[Content]
FROM
   
[#ReportContents] AS
rcBlog_LostMojo_Reports

Great, but if we have a busy Report Server we might have hundreds of reports and I may only want to find the name of a particular procedure so I can amend the 2 or 3 reports that will be affected. This is where XQuery is your friend. Now my XQuery isn't strong but I rolled up my sleeves and made a start. After a few hours of stress I called in my friend and fellow moderator at ASK SSC, Kev Riley (Twitter | Forum) who is much more familiar with XML and after a few moments of his input we had a script as below(*):

;WITH
XMLNAMESPACES(
'http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition'
AS ns1
),
ReportDefinition AS
(
SELECT name, path
,
rc.xml_content.query('//ns1:CommandText').value('(/ns1:CommandText)[1]','varchar(max)'
)
AS CommandText
,
rc.xml_content.query('//ns1:CommandType').value('(/ns1:CommandType)[1]','varchar(max)'
)
AS
CommandType
FROM
      
#ReportContents rc
)
      
SELECT
   
[Name] AS [ReportName]
,
   
[Path] AS [ReportPath]
,
   
CommandText
,
   
CommandType
FROM
   
ReportDefinition
WHERE
   
CommandText LIKE '%Employee%'

Now we get only the reports that have the specified value in the CommandText node of the RDL/XML, with the report name and its directory location in the Report Manager UI. Just what was needed. I have wrapped this code up as a stored procedure for some of the MI department where I work so that they can locate the reports they need within a few moments, that they might have previously spent hours searching for.

* Note - If you are working on a SQL 2005 Report Server, you may need to alter the line
XMLNAMESPACES(
'http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition'
to
XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition'


by fatherjack
Filed Under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Craig said:

Very nice - this will be extremely useful.

One minor typo - your last SELECT is wrong; ReportDefinition is not a column name.

January 24, 2011 10:38 PM
 

fatherjack said:

Hi Craig, ReportDefinition that I reference in the select is the name I gave to a CTE that holds some abstracted data. The code should execute with that in place. Let me know if you have any problems.
January 25, 2011 11:25 PM
 

fatherjack said:

Craig, my most sincere apologies I have got home and re-read my TSQL. I didnt spot the ReportDefinition in the WHERE clause. I have amended it so that it should be fine now. Thanks for  spotting the error. Jonathan
January 26, 2011 10:38 PM

What do you think?

(required) 
(optional)
(required) 

About fatherjack

DBA since 1999 working for not-for-profit company. http://twitter.com/fatherjack,
Latest articles
Checking Out SQL Backup Pro 7’s New Automatic Backup Verification
 Wouldn't it be great to offload the daily chore of checking the integrity of your production... Read more...

Chuck Lathrope: DBA of the Day
 Chuck Lathrope was a finalist for the Exceptional DBA of the Year award in 2009. We contacted him to... Read more...

Backups, What Are They Good For?
 Pixar recently confessed, in an engaging video, that Toy Story 2 was almost lost due to a bad backup,... Read more...

C# Async: What is it, and how does it work?
 The biggest new feature in C#5 is Async, and its associated Await (contextual) keyword. Anybody who is... Read more...

SQL Server 2012 AlwaysOn
 SQL Server AlwaysOn provides a high-availability and Disaster-recovery solution for SQL Server 2012. It... Read more...