<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Dean Kalanquin's Reporting Services Blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/deanka/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/deanka/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 17 Apr 2019 03:20:55 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Subscriptions gone crazy, a case study example from the SQL Dev Team</title>
      <link>https://docs.microsoft.com/archive/blogs/deanka/subscriptions-gone-crazy-a-case-study-example-from-the-sql-dev-team</link>
      <pubDate>Tue, 17 Apr 2012 23:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Dean Kalanquin-ExMSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/deanka/2012/04/17/subscriptions-gone-crazy-a-case-study-example-from-the-sql-dev-team/</guid>
      <description><![CDATA[Hey There -
We have a production instance of SQL Server Reporting Services to support the reporting...]]></description>
      <content:encoded><![CDATA[<p>Hey There -</p>
<p>We have a production instance of SQL Server Reporting Services to support the reporting needs of the SQL Business Unit. <a href="https://blogs.msdn.com/b/jtarquino/">Jaime Tarquino</a>&nbsp;on my team now owns the server, and we ran into an issue a couple weeks ago I worked with him on solving. It's been a while since I blogged so I thought I would share this issue with you in hopes you can learn from it, and let you know that we in the dev team also understand the experience of dealing with production system issues that affect our business and our regularly scheduled work.</p>
<p>We came to work for a busy day of meetings and test stuff as usual, but on this day we started seeing emails from users saying "why did I get 8 copies of this report in email today?". Usually subscription problems manifest themselves by <strong>not</strong> getting delivered to people's InBoxes, now they were getting <strong>8</strong> (EIGHT!) versions of the same report. So we knew something new was up.</p>
<p>Our implementation of RS is configured as a 2-node NLB farm with the RS instances installed on separate machines, and the catalog database on a 3rd machine. We typically run the latest released version of RS on the box, or something close to it, which includes CTP, RTM, and SP releases of RS, so we upgrade the server several times per year. During such an upgrade we typically follow a process similar to:</p>
<ul>
<li>Make a backup of the RS catalog database and the encryption key from the production machine</li>
<li>Restore the catalog database to SQL on another machine</li>
<li>Install&nbsp;the original, production version of RS on the test machine and configure it to use that instance of SQL Server for the catalog, and restore the encryption key</li>
<li>Verify a few reports and other features are working correctly</li>
<li>Upgrade RS to the new version</li>
<li>Verify a few reports and other features are working correctly</li>
<li>If so, perform the upgrade process on the production instance</li>
</ul>
<p>As we had just finished up the release of SQL Server 2012, we had followed these steps to upgrade our production server, but ran into an issue in step 5, "Upgrade RS to the new version". The old version of RS was an internal build and was only a minor version upgrade, so setup could not upgrade the instance, and we had to abort the upgrade process until the next week.</p>
<p>We figured our current problems were related to something that happened during the upgrade testing, and we started investigating the issue using some of the techniques I've covered in <a href="https://blogs.msdn.com/b/deanka/archive/2009/01/13/diagnosing-and-troubleshooting-subscriptions.aspx">this</a> blog. The first thing we noticed was that there were a lot (1000's) of subscriptions queued up in the Events table to be processed, and there were thousands of entries in the trace log file with this error:</p>
<p style="padding-left: 30px;">"Schedules, events, and subscriptions processing may be delayed because processing queue reached the maximum item count = &lt;nn&gt;. Items currently being processed: &lt;nn&gt;."</p>
<p>We added the logging of this error in our previous release to help identify when the queue is not getting processed.</p>
<p>Both of these are a clear sign that something is <strong>not right, </strong>but&nbsp;it only explains why reports would <strong>not</strong> be delivered, it does not explain why 8 copies were being delivered. As an interim solution to avoid the "why am I not getting my report?" emails from our users, we finally deleted the older entries from the Events table, it was clear that the backup was so large that RS could not keep up with them, and it was even falling farther behind.</p>
<p>Along the way, we'd noticed a few other facts that did not seem relevant or related to the issues at the time:</p>
<ul>
<li>There were entries in the trace log file for successful subscriptions (based on their schedule IDs), but not all had the records in the execution log table we would expect from the execution of the subscription's report</li>
<li>One of the RS server's trace log file had&nbsp;excessive numbers of&nbsp;"Logon failure: unknown user name or bad password"&nbsp;errors</li>
<li>One&nbsp;of the rogue subscriptions had been disabled two weeks prior</li>
<li>Of all the subscriptions (100's) on our production box, only a few were delivering extra copies of the report</li>
</ul>
<p>Well into the evening Jaime noticed that one of our instances of RS was configured to use the instance of SQL Server with the backup copy of the catalog database, not the production catalog database. This was the smoking gun. We have a new guy in our IT department that owns and supports the systems, it was a common human error.&nbsp;Things started to fall into place; it was pretty clear that we had two instances of RS processing the same subscription in two different catalogs.</p>
<p>This would explain <strong>one</strong> extra version of the report being delivered, but&nbsp;not 8, and and why were the old subscriptions still being processed?</p>
<p>As it turns out, the backup of the catalog database used for testing was fairly old, which explains why the old subscriptions were still firing. And apparently SQL Agent had been running on the machine for a while when RS wasn't, which put a backlog of entries in the Events table that were not being processed by RS. And when the RS service using that database was started, it began processing the backlog Event table entries for old subscriptions.</p>
<p>Also, the reports from the rogue subscriptions were all using data sources with SQL Authentication and SQL logins that did not have expiring passwords. The rest of the subscription/report datasources used Windows authentication, and the passwords in those data sources had all expired. Which was probably a <strong>good</strong> thing for us in the RS team; otherwise there would have been literally hundreds of users in the SQL Business Unit and elsewhere in Microsoft getting peppered with old and duplicate email subscription deliveries.</p>
<p>The remaining issue was the cause of the backlog of unprocessed subscriptions in our production catalog database. To be honest, I'm not sure I completely understand the cause of that issue; we lost a lot of evidence when we manually deleted the Events table, so the rest here is mostly speculation and educated guesses that I'm confident can explain the issue.</p>
<p>We did recall that several of the entries in the Notification table&nbsp;- for subscriptions currently being processed -&nbsp;in the production catalog database had been there for hours. We've had problems with runaway queries in reports in the past, basically infinite loops in report dataset TSQL code, so several of the threads had been tied up for a long time.&nbsp;It was not likely related to the upgrade&nbsp;process.</p>
<p>Our RS installation normally uses and needs two instances of RS to keep up with both subscriptions and ad-hoc reports during busy parts of the day.&nbsp;A single instance can successfully struggle to keep up, but performance does suffer and this likely contributed to the problem.</p>
<p>For the subscriptions that were running simultaneously on both instances of RS, the instance of SQL Server processing the same dataset queries in parallel likely suffered from locking and contention and that caused those reports to take longer than usual in the subscription.</p>
<p>And finally, many of the queued entries in the Events table were from data driven subscriptions. Data driven subscriptions will put one entry in the Events table for each row in the DDS query, so we likely had some unlucky timing relative to a heavy DDS schedule.</p>
<p>So in case it's not obvious, the lesson we learned is that when you are using a parallel test system like we do, make sure that the production and test RS server instances are both configured to use the correct instance of SQL for the catalog database.</p>
<p>I hope you find this interesting and useful.</p>
<p>Thanks, Dean</p>
<p>This posting is provided "AS IS" with no warranties, and confers no rights.</p>]]></content:encoded>
    </item>
    <item>
      <title>Case Study: Slow Datasource Server Killed Subscriptions on My Reporting Services Server</title>
      <link>https://docs.microsoft.com/archive/blogs/deanka/case-study-slow-datasource-server-killed-subscriptions-on-my-reporting-services-server</link>
      <pubDate>Mon, 04 Oct 2010 23:44:04 GMT</pubDate>
      <dc:creator><![CDATA[Dean Kalanquin-ExMSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/deanka/2010/10/04/case-study-slow-datasource-server-killed-subscriptions-on-my-reporting-services-server/</guid>
      <description><![CDATA[Hey There - I had an experience with our production server recently that I thought I'd share with...]]></description>
      <content:encoded><![CDATA[<p>Hey There - I had an experience with our production server recently that I thought I'd share with you, explain what we looked at to figure out what happened, and what I did to solve it. </p>
<p>To give you an overview, I am the Report Server test lead in the Reporting Services team, and I'm the primary contact for the Reporting Services Server that supports the SQL Business Unit here at Microsoft. If&nbsp;you are not already familar with the mechanics of how subscriptions work, please review my other "Troubleshooting Subscriptions" blog postings, the information will below will make much more sense.</p>
<p><strong><span style="text-decoration: underline;">Introduction</span></strong></p>
<p>When I came to work one monday morning, I noticed that I didn't receive the reports I normall get every morning,&nbsp;not the best way to start the week, I knew something was up. I have a report I created to monitor subscriptions in my server similar to the info in my other blog postings, and it showed that there were 8,000 rows in the Event table in our ReportServer database. The first indication here was that SQL Server Agent was running correctly because&nbsp;it is the source of the 8k records, but since there were no rows in the Notifications table, I knew that Reporting Services was not processing them as fast as they were coming in. One of my developer peers was helping me with the problem, he looked into the trace log files and noticed there there were entries similar to:</p>
<p><span style="color: #0000ff;">w WARN: Schedules, events, and subscriptions processing may be delayed because processing queue reached the maximum item count = N. Items currently being processed:</span></p>
<p>This is a confirmation of the problem I'd suspected, but I belive this error is only logged with SQL Server 2008R2 and beyond.</p>
<p><strong>Note:</strong> I use the term "subscription" here alone, there were also "cache refresh" Events in the table and in my queries below.&nbsp;These are from the scheduled shared dataset cache refresh feature in SQL Server 2008R2, but the process for identifying and solving them are the same so I'll just say "subscription" from here on out. </p>
<p><strong><span style="text-decoration: underline;">Background</span></strong></p>
<p>There is a database application that is most commonly used by reports hosted in our report server, and many of those reports have one or more subscriptions to deliver reports via email. That database server had been recently upgraded (and caused all kinds of other problems:) so I was suspicious that it was the source of the problem here. But to narrow it down, we had to learn a bit more about what entries were in the Event table to definitively trace it down to the problem, and we used the first query below to see which reports were potentially causing the subscription problems. After running the reports manually, there were a few that never seemed to return, and they were early in the queue, so they were the smoking gun. </p>
<p>Ultimately, the problem did end up being related to the database upgrade. The database the reports were originally written against had numerous custom indexes added by my peers with great query optimization skills. The database upgrade involved low level table schema changes, there were views based on those tables that were used by the report queries, and many of the custom indexes were not recreated during the upgrade. Hence there were many report queries that were now running much slower, by an order of magnitude, after the upgrade, and this caused the log jam in our subscription processing. There were also many other reports not affected by the database upgrade, they were either unaffected by it, or they were written against another database, but they were caught up in the backlog of the subscriptions for the slow reports. </p>
<p>At this point, all of the available polling threads in the Report Server were busy processing the subscriptions based on the reports with slow queries.&nbsp;Even more events were getting loaded into the Event table faster than&nbsp;the Report Server could process them. It was clear that the report server would not be able to recover on its own, and it was only a matter of time before executive emails would start going around asking what was going on. So there were a few things that needed to be done to get the system back into production. </p>
<p><strong><span style="text-decoration: underline;">Steps to Recover</span></strong></p>
<p><span style="text-decoration: underline;">Disable the subscriptions causing the problems in the first place</span></p>
<p>
<p>With 8k Events waiting to be processed, adding more new Events to the table would not help. And even after the current subscription queuing problem was solved, the problem would only come back if the same subscriptions continued to fire. To get an idea which reports had the most slowdown after the upgrade I created&nbsp;a report based on the second query below to get the average execution time of the reports before and after the upgrade. Thankfully there were only 20 or so reports that were causing the worst problems, and quite a few of these turned out to be invalid or outdated due to people leaving the team and so on. For each of these, I went to the report in Report Manager, and either set the subscription to expire yesterday, or deleted them outright if they appeared to be old or invalid. </p>
</p>
<p><span style="text-decoration: underline;">Clear out the backlog of queued events</span></p>
<p>Now that I was comfortable the offending report subscriptions were disabled, the Report Server still needed to crank through all of the queued events. I knew I had to purge them since many of the events were days old and would not provide value. Reporting Services does not have exposed features for this purpose, I'd have to roll up my sleeves and attack it at the database level. </p>
<p><strong>Disclaimer</strong>: we do not officially support updating the Reporting Services tables mentioned below, do so at your own risk and only in extreme production stoppage situations like I describe here. </p>
<p>Now that I had a pretty good list of the offending reports/subscriptions, I manually deleted the rows for the slow reports from the Event table with a query similar to:</p>
<p><span style="color: #0000ff;">
<pre class="scroll"><code class="cplusplus">DELETE Event WHERE EventData = &lt;guidvalue&gt;</code></pre>
</span></p>
<p>The rows that were associated with a subscription have the SubscriptionId uniqueidentifier value in the EventData column, and those associated with a scheduled cache refresh had the ReportId in the EventData column. </p>
<p>Now that I had deleted all of the Events from the offending subscriptions and reports, I removed the rows that had been entered prior to that morning with a query similar to:</p>
<p><span style="color: #0000ff;">
<pre class="scroll"><code class="cplusplus">DELETE Event WHERE TimeEntered &lt; '9/20/2010 12:00:00'</code></pre>
</span></p>
<p>This reduced the queued events to around 2,000 rows, and after the Reporting Services server was restarted to reset the polling threads, it only took about 30 minutes to process all those events. </p>
<p><strong><span style="text-decoration: underline;">Summary</span></strong></p>
<p>As a reporting services administrator, I'm lucky to be in the product group that has the experts to back me up in cases like this; the diagnosis and solution above was reviewed and understood before I did it. This is also the first and only time I've <strong>ever</strong> modified the database directly myself. My hope in providing this to you is that it will help you get out of a similar situation, but I also&nbsp;hope it doesn't encourage you to make other types of updates to the data in your Reporting Services database, or apply this solution for creative custom features involving the Events table. Much of the data in the database is very inter-related, as the test lead of this area I guarantee you that we cannot test and verify that the Reporting Services server can handle all the various types of changes that can be made directly to the database tables. So please, be careful, know your queries,&nbsp;and run them at your own risk!</p>
<p>And finally, this experience did initiate a discussion between me and my dev/pm peers as to how we can improve the Reporting Services features to handle this better in the future. </p>
<p>I hope this helps, Thanks! </p>
<p>Dean</p>
<p>&nbsp;</p>
<p><strong><span style="text-decoration: underline;">&nbsp;Event Table Query</span></strong></p>
<pre class="scroll"><code class="cplusplus"><pre class="scroll"><code class="cplusplus">-- Get the queued events associated with subscriptions
<br />select s.SubscriptionId, e.EventData, e.EventType, 
<br />'Subscription' = s.Description, 'SubscriptionOwner'=uo.UserName, 
<br />'ReportPath'= c.Path,'ReportModBy'=uc.UserName, 
<br />'MinEntered'=min(dateadd(hh,DATEDIFF(hh,GETUTCDATE(),GETDATE()),e.TimeEntered)),
<br />'EventCount'=COUNT(*)
<br />
<br />from ReportServer.dbo.Event e
<br />join ReportServer.dbo.Subscriptions s on s.SubscriptionID = e.EventData -- EventData has the subscription id
<br />join ReportServer.dbo.Catalog c on c.ItemID = s.Report_OID
<br />join ReportServer.dbo.Users uo on uo.UserID = s.OwnerID
<br />join ReportServer.dbo.Users uc on uc.UserID = c.ModifiedByID
<br />
<br />group by s.SubscriptionId, e.EventData, e.EventType, s.Description, uo.UserName, c.Path, uc.UserName
<br />
<br />union all 
<br />
<br />-- Get the queued events associated with cache refresh plans on reports
<br />select 'SubscriptionId'=null, e.EventData, e.EventType,
<br />'Subscription' = 'n/a', 'SubscriptionOwner'= 'n/a',
<br />'ReportPath'= c.Path, 'ReportModBy'=uc.UserName, 
<br />'MinEntered'=min(dateadd(hh,DATEDIFF(hh,GETUTCDATE(),GETDATE()),e.TimeEntered)),
<br />'EventCount'=COUNT(*)
<br />
<br />from ReportServer.dbo.Event e
<br />join ReportServer.dbo.Catalog c on c.ItemID = e.EventData -- EventData has the report id
<br />join ReportServer.dbo.Users uc on uc.UserID = c.ModifiedByID
<br />
<br />group by e.EventData, e.EventType, c.Path, uc.UserName
<br />
<br />order by EventCount desc
<br /></code></pre>
<br />
<br /></code></pre>
<p><strong><span style="text-decoration: underline;">Query to show the reports whose execution was much slower after the upgrade</span></strong></p>
<pre class="scroll"><code class="cplusplus">select
<br />'ItemId'=post.ItemID, 'Path'=post.Path, 'LastModBy'=post.LastModBy, 
<br />'preExecCount'=pre.ExecCount, 'postExecCount'=post.ExecCount, 
<br />'preAvgData'=pre.AvgData, 'postAvgData'=post.AvgData,
<br />'pcntDiff'= case when pre.AvgData = 0 then 0.0 else ((post.AvgData-pre.AvgData)*1.0)/(pre.AvgData*1.0) end
<br />from
<br />(
<br /> select
<br /> c.ItemID, c.Path, 'LastModBy'=um.UserName,
<br />
<br /> 'ExecCount'=COUNT(*),
<br /> 'MinData'=MIN(TimeDataRetrieval/1000), 
<br /> 'MaxData'=MAX(TimeDataRetrieval/1000), 
<br /> 'AvgData'=AVG(TimeDataRetrieval/1000)
<br />
<br /> from ReportServer.dbo.ExecutionLogStorage el
<br /> join Catalog c on c.ItemID = el.ReportID
<br /> join Users um on um.UserID = c.ModifiedByID
<br /> where 
<br /> el.ReportAction = 1 -- Render
<br /> and el.Source = 1 -- Live
<br /> and el.TimeStart between '9/16/2010' and '9/25/2010'
<br /> group by c.ItemID, 
<br /> c.Path, 
<br /> um.UserName
<br />
<br />) as post
<br />
<br />join 
<br />
<br />(
<br /> select  c.ItemID, c.Path, 'LastModBy'=um.UserName,
<br /> 'ExecCount'=COUNT(*),
<br /> 'MinData'=MIN(TimeDataRetrieval/1000), 
<br /> 'MaxData'=MAX(TimeDataRetrieval/1000), 
<br /> 'AvgData'=AVG(TimeDataRetrieval/1000)
<br /> from ReportServer.dbo.ExecutionLogStorage el
<br /> join Catalog c on c.ItemID = el.ReportID
<br /> join Users um on um.UserID = c.ModifiedByID
<br /> where 
<br /> el.ReportAction = 1 -- Render
<br /> and el.Source = 1 -- Live
<br /> and el.TimeStart between '9/9/2010' and '9/15/2010'
<br /> group by c.ItemID, 
<br /> c.Path, 
<br /> um.UserName
<br />
<br />) as pre on pre.ItemID = post.ItemID
<br />where case when pre.AvgData = 0 then 0.0 else ((post.AvgData-pre.AvgData)*1.0)/(pre.AvgData*1.0) end &gt; 1.0
<br />order by  case when pre.AvgData = 0 then 0.0 else ((post.AvgData-pre.AvgData)*1.0)/(pre.AvgData*1.0) end desc
<br />
<br /></code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Troubleshooting Subscriptions: Part II, Using the Reporting Services Trace Log File</title>
      <link>https://docs.microsoft.com/archive/blogs/deanka/troubleshooting-subscriptions-part-ii-using-the-reporting-services-trace-log-file</link>
      <pubDate>Mon, 15 Feb 2010 20:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Dean Kalanquin-ExMSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/deanka/2010/02/15/troubleshooting-subscriptions-part-ii-using-the-reporting-services-trace-log-file/</guid>
      <description><![CDATA[Hey There – About a year ago I posted this blog about troubleshooting subscriptions. Since then...]]></description>
      <content:encoded><![CDATA[<SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Hey There – About a year ago I posted <A href="https://blogs.msdn.com/deanka/archive/2009/01/13/diagnosing-and-troubleshooting-subscriptions.aspx" mce_href="https://blogs.msdn.com/deanka/archive/2009/01/13/diagnosing-and-troubleshooting-subscriptions.aspx">this</A> </FONT><FONT size=3 face=Calibri>blog about troubleshooting subscriptions. Since then I’ve received quite a few questions and come across more subscription problems in our production Reporting Services server that identified a few gaps in my previous posting, so I thought I would elaborate more on the subject here. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>To recap a few key points from before:</FONT></P>
<P><SPAN><U>When you create a subscription several things are added to the RS server:<o:p></o:p></U></SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; FONT-SIZE: 11pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-latin">- A row is placed in the Subscriptions table…<o:p></o:p></SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; FONT-SIZE: 11pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-latin">- A row is placed in the Schedule and ReportSchedule tables…<o:p></o:p></SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; FONT-SIZE: 11pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-latin">- A SQL Server Agent job is created…<o:p></o:p></SPAN></P>
<P><STRONG><U><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; FONT-SIZE: 11pt; FONT-WEIGHT: normal; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-latin; mso-bidi-font-weight: bold">When the subscription runs several things happen:</SPAN></U></STRONG><B style="mso-bidi-font-weight: normal"><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; FONT-SIZE: 11pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-latin"><o:p></o:p></SPAN></B></P>
<P><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; FONT-SIZE: 11pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-latin">- The SQL Server Agent job fires and puts a row in the Event table…<o:p></o:p></SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; FONT-SIZE: 11pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-latin">- The RS server service has a limited number of threads (2 per CPU) that poll the Event table…<o:p></o:p></SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; FONT-SIZE: 11pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-latin">- When it finds an event, it puts a row in the Notifications table and <STRONG>starts processing</STRONG>…<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>That blog content is a good start at isolating the issue to one of the key sources of problems, but since then I’ve found that many of the subscription problems I deal with fall into the “<STRONG>starts processing…</STRONG>” point above. </FONT><FONT size=3 face=Calibri>I often find my investigation of these problems go well beyond my “this can usually be tested by running the report manually” recommendation, so I’ll attempt to give this area better justice here by sharing some of my experience with the trace log file.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT size=3><FONT face=Calibri><U>The Trace Log: More Than You Ever Needed or Wanted to Know About Your RS Server<o:p></o:p></U></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The trace log file is created in a folder under your SQL Server installation path. In my machine this happens to be:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER\Reporting Services\LogFiles<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>for a default install of SQL Server 2008 R2. Previous versions of SQL Server will be named differently, but your’s should be in a similar location. A quick search of the SQL Server books online will give you some configuration options that allow you to control how big the files get, how long they are kept, and the type of information and activities that are logged to the file and so on. For a detault RS installation, these files will be limited to a max size of 32mb and “rollover” to a new file if that size is exceeded, and old files will be deleted after 14 days. The file will also be closed and recreated each time the RS server is recycled, which by default will happen every day at midnight. The name of the trace log file will be similar to "ReportServerService__02_15_2010_12_56_37.log", and the “02_15_2010_12_56_37” portion will indicate the date/time the file was created in the “MM_DD_YYYY_HH_mm_SS” format. If you happen to be researching a problem that just ocurred, you can just view the newest file, otherwise you will have to find the file that contains the range of information&nbsp;including the entries you are looking for, based on the timestamps in the file names and the entries in the file. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>A common response when people ask for help with a report problem is, “What did you see in the trace log file?”. However, it can be a bit daunting the first time you open the file, and it can take some practice to get good at reading it effectively. The first problem is the sheer bulk of content that is logged in the multi-mb file, and the next problem is the cryptic nature of the multi-mb content. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>I’d like to help you make sense of this information to&nbsp;diagnose and fix subscription and other Reporting Services problems. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>This file contains a multitude of information about&nbsp;practically every action, operation, process etc that occurs in the RS server. Some of them are for internal processing, and some are related to the processing of reports, subscriptions or other user initiated or defined operations. For example:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">library!WindowsService_9!12c0!02/15/2010-02:00:16:: e ERROR: Throwing Microsoft.ReportingServices.Diagnostics.Utilities.LogonFailedException: Logon attempt for user 'username' failed., Microsoft.ReportingServices.Diagnostics.Utilities.LogonFailedException: Log on failed. Ensure the user name and password are correct. ---&gt; System.ComponentModel.Win32Exception: Logon failure: unknown user name or bad password</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt"></SPAN><FONT size=3 face=Calibri>There are several aspects of this entry that are worth noting. First of all, each "entry" is prefixed by a set of fields similar to:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">processing!WindowsService_9!12c0!02/15/2010-02:00:16::</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Which can be parsed down to:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri><SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "</SPAN>Processing"<SPAN style="mso-tab-count: 2">: </SPAN>The library within the RS server</FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri><SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "</SPAN>WindowsService_9":The AppDomain within the library</FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri><SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "</SPAN>12c0"<SPAN style="mso-tab-count: 3">: </SPAN>The thread within the Appdomain</FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri><SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "</SPAN>02/15/2010-02:00:16<SPAN style="mso-tab-count: 1">": </SPAN>The timestamp when the information was logged</FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri>Secondly, most entries contain the strings “I Info”, “w Warn”, or “e Error”; the “e Error” entries are usually the most interesting and valuable, yet the other entries around the error entries can also contain interesting information. </FONT></FONT><FONT size=3 face=Calibri>The remaining parts of the entry line describe what occurred in the server.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>And finally, as you will see below, there is sometimes a set of callstack frames following (what I call) the regular entry lines, and they typically have the form:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>These entries refer to the code inside the Reporting Services server and its referenced libraries. They&nbsp;will not typically be of much value to you in researching report or subscription errors, but if you do happen to experience other types of RS server problems (or yes, even server bugs we may have unfortunately missed in testing), including the callstack with an explanation of the problem will be very helpful for us or CSS to understand more about the problem. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><U><FONT face=Calibri><FONT size=3>Locating Subscription Entries in the Trace Log File<o:p></o:p></FONT></FONT></U></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Step 1: Get the Schedule Id<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>A good general approach to researching problems in the trace log file is to determine when a problem occurred, find the entries in the log file around that time, and just start poking around. This can be enough if you have a quiet RS server with very little activity, but an active server can have hundreds of entries within a few minutes, so “poking around” may not be too successful. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The next step would be to get a more precise date/time value for when a problem occurred, this can be obtained from the TimeStart or TimeEnd fields in the ExecutionLog table, or the LastRunTime field of the Subscription table in the catalog database. But again, even with this information, there may still be hundreds of entries in the trace log file at key times (say, 8am) when there is a lot of other successful and unsuccessful subscription&nbsp;or report activity in the server at the same time as the failed subscription.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The key point in isolating subscriptions further goes back to what happens “When a subscription runs”. As I said before, when a subscription runs, the status in the subscription table is updated, but I should have added “<I style="mso-bidi-font-style: normal">and</I> <I style="mso-bidi-font-style: normal">entries are placed in the trace log file with the schedule id.”</I> In other words, the key link between a subscription execution and the entries in the trace log file is the schedule&nbsp;id for the subscription. The schedule&nbsp;id must be obtained from the Subscription table, it is not visible via the UI tools, and the query below can help, assuming you understand the legal stuff:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><STRONG><SPAN style="FONT-FAMILY: 'Calibri','sans-serif'; mso-ansi-language: EN; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-bidi-theme-font: minor-bidi; mso-bidi-font-family: 'Times New Roman'" lang=EN>Disclaimer</SPAN></STRONG><SPAN style="mso-ansi-language: EN" lang=EN><FONT face=Calibri>: we do not officially support querying all of the Reporting Services tables mentioned below, the columns and information in them may change in the future.</FONT></SPAN></FONT></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: blue; FONT-SIZE: 10pt; mso-no-proof: yes">select</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: red; FONT-SIZE: 10pt; mso-no-proof: yes">'SubnDesc'</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <SPAN style="COLOR: gray">=</SPAN> s<SPAN style="COLOR: gray">.</SPAN><SPAN style="COLOR: blue">Description</SPAN><SPAN style="COLOR: gray">,<o:p></o:p></SPAN></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: red; FONT-SIZE: 10pt; mso-no-proof: yes">'SubnOwner'</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <SPAN style="COLOR: gray">=</SPAN> us<SPAN style="COLOR: gray">.</SPAN>UserName<SPAN style="COLOR: gray">,<o:p></o:p></SPAN></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: red; FONT-SIZE: 10pt; mso-no-proof: yes">'LastStatus'</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <SPAN style="COLOR: gray">=</SPAN> s<SPAN style="COLOR: gray">.</SPAN>LastStatus<SPAN style="COLOR: gray">,<o:p></o:p></SPAN></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: red; FONT-SIZE: 10pt; mso-no-proof: yes">'LastRun'</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <SPAN style="COLOR: gray">=</SPAN> s<SPAN style="COLOR: gray">.</SPAN>LastRunTime<SPAN style="COLOR: gray">,<o:p></o:p></SPAN></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: red; FONT-SIZE: 10pt; mso-no-proof: yes">'ReportPath'</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <SPAN style="COLOR: gray">=</SPAN> c<SPAN style="COLOR: gray">.</SPAN><SPAN style="COLOR: blue">Path</SPAN><SPAN style="COLOR: gray">,<o:p></o:p></SPAN></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: red; FONT-SIZE: 10pt; mso-no-proof: yes">'ReportModifiedBy'</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <SPAN style="COLOR: gray">=</SPAN> uc<SPAN style="COLOR: gray">.</SPAN>UserName<SPAN style="COLOR: gray">,<o:p></o:p></SPAN></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><B style="mso-bidi-font-weight: normal"><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: red; FONT-SIZE: 10pt; mso-no-proof: yes">'ScheduleId'</SPAN></B><B style="mso-bidi-font-weight: normal"><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <SPAN style="COLOR: gray">=</SPAN> rs<SPAN style="COLOR: gray">.</SPAN>ScheduleId<SPAN style="COLOR: gray">, -- This is what you need<o:p></o:p></SPAN></SPAN></B></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: red; FONT-SIZE: 10pt; mso-no-proof: yes">'SubscriptionId'</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> <SPAN style="COLOR: gray">=</SPAN> s<SPAN style="COLOR: gray">.</SPAN>SubscriptionID<o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: blue; FONT-SIZE: 10pt; mso-no-proof: yes">from</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> ReportServer<SPAN style="COLOR: gray">.</SPAN>dbo<SPAN style="COLOR: gray">.</SPAN>Subscriptions s<o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: gray; FONT-SIZE: 10pt; mso-no-proof: yes">join</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> ReportServer<SPAN style="COLOR: gray">.</SPAN>dbo<SPAN style="COLOR: gray">.</SPAN><SPAN style="COLOR: blue">Catalog</SPAN> c <SPAN style="COLOR: blue">on</SPAN> c<SPAN style="COLOR: gray">.</SPAN>ItemID <SPAN style="COLOR: gray">=</SPAN> s<SPAN style="COLOR: gray">.</SPAN>Report_OID<o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: gray; FONT-SIZE: 10pt; mso-no-proof: yes">join</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> ReportServer<SPAN style="COLOR: gray">.</SPAN>dbo<SPAN style="COLOR: gray">.</SPAN>ReportSchedule rs <SPAN style="COLOR: blue">on</SPAN> rs<SPAN style="COLOR: gray">.</SPAN>SubscriptionID <SPAN style="COLOR: gray">=</SPAN> s<SPAN style="COLOR: gray">.</SPAN>SubscriptionID<o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: gray; FONT-SIZE: 10pt; mso-no-proof: yes">join</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> ReportServer<SPAN style="COLOR: gray">.</SPAN>dbo<SPAN style="COLOR: gray">.</SPAN>Users uc <SPAN style="COLOR: blue">on</SPAN> uc<SPAN style="COLOR: gray">.</SPAN>UserID <SPAN style="COLOR: gray">=</SPAN> c<SPAN style="COLOR: gray">.</SPAN>ModifiedByID<o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 0pt; mso-layout-grid-align: none" class=MsoNormal><SPAN style="FONT-FAMILY: 'Courier New'; COLOR: gray; FONT-SIZE: 10pt; mso-no-proof: yes">join</SPAN><SPAN style="FONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-no-proof: yes"> ReportServer<SPAN style="COLOR: gray">.</SPAN>dbo<SPAN style="COLOR: gray">.</SPAN>Users us <SPAN style="COLOR: blue">on</SPAN> us<SPAN style="COLOR: gray">.</SPAN>UserID <SPAN style="COLOR: gray">=</SPAN> s<SPAN style="COLOR: gray">.</SPAN>OwnerId<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Step 2: Find That Schedule&nbsp;ID in the Trace Log File<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Once you have the schedule id for the subscription, you can look for the value in the trace file to find the entries for that subscription. The good news is that the schedule id is a guid; the only entries you find in the file with that string will be the entries for this subscription. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><SPAN style="FONT-FAMILY: Wingdings; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-char-type: symbol; mso-symbol-font-family: Wingdings"><SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings">à</SPAN></SPAN><FONT face=Calibri>Side Note: subscriptions can use either a custom schedule created for that subscription, or they can use a shared schedule. I only use custom schedules, and the examples here assume a custom schedule. </FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The not-so good news is that if the subscription fired several times within the timeframe of the contents in the trace log file, you will have to find the entry nearest to the one that failed based on the timestamp of the entry. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>In my local server, I created a subscription and waited for it to fire. The rows in the Subscription table using the query above told me that my schedule id was “4459bc37-97d5-46c4-bd9d-d1e97e4e1cac”, and the first entry with this guid was:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">library!WindowsService_0!1354!02/15/2010-14:51:03:: i INFO: Schedule 4459bc37-97d5-46c4-bd9d-d1e97e4e1cac executed at 02/15/2010 14:51:03.<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><SPAN style="FONT-FAMILY: Wingdings; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-char-type: symbol; mso-symbol-font-family: Wingdings"><SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings">à</SPAN></SPAN><FONT face=Calibri>Side Note: I’m using the latest and greatest version of Reporting Services in SQL 2008 R2, and my dev partner who helps me with this stuff improved the log file content for subscriptions in this release. Your log file content may be somewhat different, but the approach is the same. </FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT size=3><FONT face=Calibri>Step 3: Look at the other entries with the same thread in the same time proximity<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Based on the entry above with the schedule id, you can see that the thread is “1354”, and searching on that thread, you will see another set of entries similar to:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt; mso-bidi-theme-font: minor-bidi; mso-bidi-font-family: 'Times New Roman'; mso-bidi-font-size: 11.0pt">schedule!WindowsService_0!1354!02/15/2010-14:51:03:: Creating Time based subscription notification for subscription: 41344f16-88e5-43b2-9e6b-1996e210eb6b<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt; mso-bidi-theme-font: minor-bidi; mso-bidi-font-family: 'Times New Roman'; mso-bidi-font-size: 11.0pt">library!WindowsService_0!1354!02/15/2010-14:51:03:: i INFO: Schedule 4459bc37-97d5-46c4-bd9d-d1e97e4e1cac execution completed at 02/15/2010 14:51:03.<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt; mso-bidi-theme-font: minor-bidi; mso-bidi-font-family: 'Times New Roman'; mso-bidi-font-size: 11.0pt">notification!WindowsService_0!1354!02/15/2010-14:51:03:: i INFO: Handling notification of subscription 41344f16-88e5-43b2-9e6b-1996e210eb6b to report Reportname via delivery provider Report Server FileShare<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt; mso-bidi-theme-font: minor-bidi; mso-bidi-font-family: 'Times New Roman'; mso-bidi-font-size: 11.0pt">library!WindowsService_0!1354!02/15/2010-14:51:04:: i INFO: RenderForNewSession('/pathname/reportname’)<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>You may notice that the subscription id guid is also in the entries here, they <EM>can</EM> also be used, but in previous versions of RS we only logged the schedule id, so I'm focusing on it here instead.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>In this example, the subscription was successful, yeah, big deal, but it <EM>is</EM> a good start in learning how to find the information when they are not successful. I also used a quiet local report server, so all my entries were literally next to each other in the file making them easy to identify. But again, in our production server, there are a lot of subscriptions firing at common times, so their entries may be spread more broadly throughout the file. Also, be careful to watch the timestamps of the entries for each thread, if the same thread happened to process another subscription later, you need to make sure you don’t associate your current subscription with the entries for the next. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><U><FONT face=Calibri><FONT size=3>Common Types of Subscription Failures<o:p></o:p></FONT></FONT></U></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Now,&nbsp;lets have more fun and look at a couple common types of subscription errors and their trace log file entries.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Invalid logins<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri>In today’s world of defense against hackers (those guys really take the fun out of developing software</FONT><SPAN style="FONT-FAMILY: Wingdings; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-char-type: symbol; mso-symbol-font-family: Wingdings"><SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings">L</SPAN></SPAN><FONT face=Calibri>) the sorry truth is that SQL passwords have to expire. This is the most common cause of recurring problems&nbsp;I see, and they often happen when people forget to update their passwords in all the necessary data sources. In my example here, I changed the password for the data source of the report used by my subscription to be invalid. The error displayed in the subscription table and in Report Manager was simply “</FONT></FONT><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">An error has occurred during report processing.</SPAN><FONT size=3 face=Calibri>” which doesn’t tell much, but the entries recorded in the trace log file included a much more useful error: </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">processing!WindowsService_0!fd8!02/15/2010-20:52:06:: e ERROR: Throwing Microsoft.ReportingServices.ReportProcessing.ReportProcessingException: , Microsoft.ReportingServices.ReportProcessing.ReportProcessingException: <B style="mso-bidi-font-weight: normal">Cannot impersonate user for data source 'datasourcename'. ---&gt;</B> Microsoft.ReportingServices.Diagnostics.Utilities.LogonFailedException: Log on failed. ---&gt; System.ComponentModel.Win32Exception: <STRONG>Logon failure: unknown user name or bad password<o:p></o:p></STRONG></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT size=3><FONT face=Calibri>Report Query Errors<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Another common theme I see when users say “my subscription is not working” is, report queries that were successful when the report is created may not always stay that way; they may later return typical TSQL errors indicating deadlocks, query timeouts, divide by zero, invalid casting and so on. In my example, the report was based on a call to a stored procedure, so I changed the procedure name in the report dataset to an invalid name, and when the subscription fired Report Manager still displayed the error “</FONT><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">An error has occurred during report processing.</SPAN><FONT size=3 face=Calibri>”, but the trace file displayed this entry:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">subscription!WindowsService_0!fd8!02/15/2010-21:09:08:: i INFO: Microsoft.ReportingServices.Diagnostics.Utilities.RSException: An error has occurred during report processing. ---&gt; Microsoft.ReportingServices.ReportProcessing.ProcessingAbortedException: An error has occurred during report processing. ---&gt; Microsoft.ReportingServices.ReportProcessing.ReportProcessingException: Query execution failed for dataset 'datasetname'. ---&gt; System.Data.SqlClient.SqlException: Could not find stored procedure 'databasename.dbo.badprocname'.<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT size=3><FONT face=Calibri>Data Driven Subscription Errors<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Both of the examples above were the result of common report definition problems, and as I alluded to in my other posting, these types of problems could have been found by just running the report locally using a report designer where the errors are returned to the client. This is how I often research problems with the reports I own, because a) I don’t have to hassle with the trace log file, and b) I commonly have to fix the problem using the report designer anyways. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>But another recent case I dealt with was related to a data driven subscription. The IT admin of the server knows just enough about Reporting Services to be dangerous, and their response to me (and a large alias of RS people who watch the server) was “Why is the subscription failing when the report runs fine manually? Subscriptions are not working, please fix!”. To make matters worse, the report and subscrition permssions were locked up tight and I couldn't view it in Report Manager. </FONT><FONT size=3 face=Calibri>This is a case where you cannot just run the report to see the problem, you have to look in the trace log file. </FONT><FONT size=3 face=Calibri>For this example I created a valid data driven subscription, and the entries in the log file were similar to:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">library!WindowsService_0!fd8!02/15/2010-21:19:03:: i INFO: Schedule 2fa7246d-c112-496f-969b-680e279413d3 executed at 02/15/2010 21:19:03.<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">schedule!WindowsService_0!fd8!02/15/2010-21:19:03:: Creating Time based subscription notification for subscription: 3c963022-70b7-4172-926a-f9a045a2d38f<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">library!WindowsService_0!fd8!02/15/2010-21:19:03:: i INFO: Schedule 2fa7246d-c112-496f-969b-680e279413d3 execution completed at 02/15/2010 21:19:03.<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>As you may notice, the log file entries for the data driven subscription were in a somewhat different format from regular subscriptions. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>To simulate the error, I dropped the table that was used in my data driven subscription query, and the trace log entry was:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">library!WindowsService_0!18e4!02/15/2010-21:38:03:: e ERROR: Error processing data driven subscription: System.Data.SqlClient.SqlException: Invalid object name 'dbname.dbo.testtable'.<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt"><SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Ok, the subscription status I could have queried for this particular error was also “</FONT><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">Error: Invalid object name 'dbname.dbo.testtable'.</SPAN><FONT size=3 face=Calibri>”, but I’m on such a roll with the trace log file I thought I would include it anyways.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT size=3 face=Calibri>File Delivery Errors</FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">As I was creating the subscriptions to demonstrate the log file information for this blog, I have been using file share delivery because my local box is not configured with SMTP to deliver emails, and in the process I mistakenly used the wrong password for the credentials to access the file share in my subscription. The error displayed in Report Manager was “Done: 1 processed of 1 total; 1 errors.”, but the entry in the trace log file was:</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; FONT-SIZE: 8pt">subscription!WindowsService_0!fd8!02/15/2010-21:34:10:: i INFO: Error writing file xx.xml to path <A href="file://myserver/myshare"><FONT color=#0000ff>\\myserver\myshare</FONT></A><o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt"><o:p>&nbsp;</o:p></SPAN><B style="mso-bidi-font-weight: normal"><U><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">Using the Trace Log File to Investigate Other Types of Errors<o:p></o:p></SPAN></U></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">The errors mentioned above are just a sampling of the types of problems that can affect subscriptions, many are the same as or very similar to the types of errors that affect live report execution outside of subscriptions. I won’t take blog space to cover more here, but there are many other types of problems that can be diagnosed through the trace log file. </SPAN><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">They can also be investigated using many of the same techniques I mention here, the only difference is that with problems with subscriptions, you start by searching for the schedule id. But for reports, the entries are in a form similar to:<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; COLOR: black; FONT-SIZE: 8pt">library!WindowsService_0!fd8!02/15/2010-21:34:03:: i INFO: RenderForNewSession('/pathname/reportname')<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">Problems with data sources are similar to:<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; COLOR: black; FONT-SIZE: 8pt">processing!WindowsService_0!18e4!02/15/2010-20:50:05:: e ERROR: Throwing Microsoft.ReportingServices.ReportProcessing.ReportProcessingException: , Microsoft.ReportingServices.ReportProcessing.ReportProcessingException: Cannot impersonate user for data source 'datasourcename'. ---&gt; Microsoft.ReportingServices.Diagnostics.Utilities.LogonFailedException: Log on failed. ---&gt; System.ComponentModel.Win32Exception: Logon failure: unknown user name or bad password<o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">The only difference is what you search on in the file.&nbsp;</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">And finally, many of the techniques I mention here refer to finding a starting location in the file and searching down for the entries with the same thread. Sometimes, the real problem happened before the entry you found, so if you don’t find what you need, you might want to try searching “up” through the previous entries for the thread. </SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt"><STRONG><U>Corellating the Trace Log Files With Other Data</U></STRONG></SPAN></P><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt"></SPAN><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt"><o:p>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">I've mentioned the ExecutionLog table (and its related views) in my other blogs, and my esteemed peer Robert Bruckner covers it thoroughly in his blog <A href="https://blogs.msdn.com/robertbruckner/archive/2009/01/05/executionlog2-view.aspx" mce_href="https://blogs.msdn.com/robertbruckner/archive/2009/01/05/executionlog2-view.aspx">here</A>. This information is available in the report server catalog database and it also has a wealth of valuable information about the report executions and other activities in your report server. But when errors are shown there, the only information you have is the status of the report execution, ie "rsProcessingAborted", so you can use the steps above to take the report name and the execution time to search for the real problem in the trace log&nbsp;file.</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">The opposite is also true, if you happen to be strolling through the log file and come across error entries like those I listed above, you can also go to the ExecutionLog table to see:</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">- What was executing around the time that the trace log entry was recorded</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">- Who was executing it and what parameters they used</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">- Whether it was&nbsp;a subscription or an interactive (live)&nbsp;request</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">- Was it executed live from a data source or did it come from the cache</SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">and so on. Note that subscription executions&nbsp;themselves are not in the ExecutionLog, only the reports that are run by the subscription are logged there. So if the subscription problem happens before the step where the report is executed, as in the data driven subscription example above, you won't find it in the ExecutionLog. The good news is that you can also </SPAN><SPAN style="LINE-HEIGHT: 115%; FONT-FAMILY: 'Arial','sans-serif'; COLOR: black; FONT-SIZE: 10pt">cross reference the entries in the trace log file with the Subscriptions table based on the Schedule Id to learn a lot more about the subscription problem. </SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><STRONG><U>So Why Can't I QUERY the @@#$% Trace Log File???:(</U></STRONG></o:p></SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN>If you've&nbsp;read my intro blog entry, I'm a database guy. The ever present thought in my mind when I'm searching and scrolling and searching and scrolling through log files is the same question, which I asked the first person who would listen to me about ten seconds after&nbsp;I first opened a trace log file. "Hey, we could write reports against it, make subscriptions on those reports for monitoring, aggregate data over time with cubes and dashboards and this and&nbsp;that and all kinds of other cool database stuff". <SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN>So if you have these same thoughts, I've got your back. </SPAN></SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN></SPAN></SPAN><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN>When I calmed down and turned my hearing back on, the key fact is that we really have to weigh costs of this sort of thing. There is a huuuuge amount of data in the trace log files, and taking the time to log them in a SQL table during report server processing will cause a pretty big hit on the resources of the RS server and its catalog database, not to mention the costs of cleaning up old entries to prevent the catalog database files from filling up the disk and killing the whole server, and a few other issues we probably wouldn't realize and have to address&nbsp;until after&nbsp;it is all in place. </SPAN><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN>We could trim the amount of data sent to the database table, but much of the content really is valuable, so what should we give up? </SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN>I did talk to an IS&nbsp;firend about using their features to load the file into the database, he said "Standard format? Check, no problem. <EM>Occasional</EM> callstacks?...", and that plan fell apart. Finally, I did create a TSQL parser and a snappy database design to process and store it for queries, and my more coding-oriented peers eventually shamed me into doing it with C#. Its pretty cool and useful, but not quite bulletproof enough for prime time yet. I may decide to post this solution at some point in the future, assuming&nbsp;I finish writing performance reviews in time and make space in my deliverables for the <EM>next</EM> cool release of RS. </SPAN></P></SPAN>
<P><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN>Happy Reporting! I hope this helps, please let me know.</SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN>Thanks, Dean<o:p></o:p></SPAN></P>
<P><SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt; mso-ansi-language: EN" lang=EN>As usual, "This blog is provided 'AS IS' with no warranties, and confers no rights."</SPAN></o:p></SPAN></P>]]></content:encoded>
    </item>
    <item>
      <title>Tips for Handling Tricky TSQL Problems in Report Dataset Queries</title>
      <link>https://docs.microsoft.com/archive/blogs/deanka/tips-for-handling-tricky-tsql-problems-in-report-dataset-queries</link>
      <pubDate>Thu, 22 Oct 2009 09:55:00 GMT</pubDate>
      <dc:creator><![CDATA[Dean Kalanquin-ExMSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/deanka/2009/10/22/tips-for-handling-tricky-tsql-problems-in-report-dataset-queries/</guid>
      <description><![CDATA[Hey There – It’s been a while since I woke up my blog, and I've seen a pattern of questions in the...]]></description>
      <content:encoded><![CDATA[<SPAN style="FONT-FAMILY: 'Arial','sans-serif'; FONT-SIZE: 10pt"><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>Hey There – It’s been a while since I woke up my blog, and I've seen a pattern of questions in the forums and some of the report writers I work with, so I thought I'd post some suggestions for dealing with tricky TSQL problems in Report Datasets.<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><U><FONT face=Calibri><FONT size=3>Types of problems I see people encounter <o:p></o:p></FONT></FONT></U></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>Most report writers start off their efforts with relatively straitforward SELECT statements, format it in a table and, voila!, a killer report. Then inevitably, a report consumer will respond with something similar to "Well, it looks <EM>pretty</EM> good, <EM>but</EM> I'd like to have it........", and the next thing you know, the reports have long lists of parameters, hundreds of lines of TSQL in report queries, spread across numerous dimensions of linked / dependent reports, parameter customized TSQL and so on; it can get out of control quickly. To be more specific:<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Multi Value Parameters<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>A multi value parameter must be built into a list that is used as "IN (@parmname)" in a dataset query. When SQL Server executes the statement, it needs the list to look very neat and clean similar to (1,2,3,4...) or (a,b,c...). This works fine when you have a nice clean list of multi-parameter values like "1,2,3" or "a,b,c" but when you start populating the multi-value parameter with a query, and the query returns non-int datatypes, nulls, and strings that contain spaces, quotes, or commas, this can become a hairpulling, frustrating experience. <o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Large TSQL Batches<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>Several of my reports use queries that join 10+ tables, with derived tables, temp tables, cursors and so on. Given the size of the query, it can become a maintenance nightmare when I need to tweak it a bit. The report designers have pretty primitive TSQL query editors, and I always seem to forget all the places they need to be changed across numerous datasets and reports. <o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Hierarchical Reports/Queries<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>I often have a main report that shows aggregated information for a Group, it will have drillthroughs to a report aggregated for a Team, with another drillthrough to the details for an Individual. Considering that these three reports all use the same basic "Large TSQL Batch" I described above, and they only differ in the aggregation level of the query and the grouping design in the report, when a minor tweak needs to be made to the query logic, the maintenance nightmare is compounded by the number of affected/related reports.<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Static TSQL Limitations<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>When the complexity of the report grows with many parameters and sorting/filtering logic requirements, static TSQL queries can be limiting and can start performing very poorly. For example, parameters can be used to simulate ad-hoc query logic similar to "show me the data for the last 7 days or for the last 24 hours", "I want to sort by columns a,b,c or by c,b,a", "I would like all the data between x and y, or &gt; x, or &lt;= y". Trying to support these types of ad-hoc logic in a static query can either require lots of "OR" logic (which is bad for getting good query plans from the SQL Server query optimizer), or may just be impossible.<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><U><FONT face=Calibri><FONT size=3>Tips for dealing with them<o:p></o:p></FONT></FONT></U></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Code Your Queries in SQL Management Studio, not in the Report Designers<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>I've spent a great deal of my database career living with the various TSQL interfaces and editors; first was the DOS prompt OSQL/ISQL command line editors ("They're all I'll EVER need"-yeah right), then the revolutionary Windows-based ISQLw editor that was basically Notepad with the ability to run the query, then the SQL Enterprise Manager allowed you to write queries and manage the database with a GUI, and finally today's SQL Server Management Studio (aka SSMS here) does all that with live, context-sensitive syntax verification, auto-suggested object names, and efficient handling of large result sets.<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>The reason I mention all this is to point out that my peers in the next building have built some very powerful query writing tools, and while my peers down the hall have built powerful report creation tools, the report designers are not meant to be powerful query writing tools. If you find youself coding a lot of large queries in the report designers, <STRONG><EM>please</EM></STRONG>, get into the habit of copying/pasting the report queries to/from the designer and SSMS. Once you get used to declaring/setting parameter values when running in SSMS, and then commenting them out in the designers, life will become much more pleasant for you. <o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Pre-Calculating Parameter Values in TSQL Variables<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Report parameters are most commonly used in queries similar to</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>SELECT .... WHERE datecolumn &lt; @maxdateparm"</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p><FONT face=Calibri><FONT size=3>In the "show me the data for the last 7 days or for the last 24 hours" example above, this could be coded in the dataset query as:<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>SELECT ... FROM ... WHERE <o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>(@datenameparm = 'Days' and datecolumn&nbsp;&gt; DATEADD(dd,@datevalueparm,GETDATE())<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>or <o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>(@datenameparm = 'Hours' and datecolumn&nbsp;&gt; DATEADD(hh,@datevalueparm,GETDATE())<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT face=Calibri><FONT size=3>However when you start OR'ing the queries like this they become much less efficient. An alternative would be to declare and set a TSQL variable in the dataset query outside the SELECT statement, and then use it in the SELECT statement, for example:<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>DECLARE @mindate DATETIME<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>SELECT @mindate = <o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>CASE WHEN @datenameparm = 'Days' THEN DATEADD(dd,@datevalueparm,GETDATE()) <o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>ELSE DATEADD(hh,@datevalueparm,GETDATE()) end<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>SELECT ... FROM ... WHERE datecolumn &gt;@mindate<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT face=Calibri><FONT size=3>Moving Queries into Views and Stored Procedures<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Bear in mind that a dataset query is just a batch of TSQL statements that the report processing engine sends blindly to SQL Server. A long as SQL Server returns a result set, the report processing engine doesn’t know or care what is in the batch; it can be anything from a simple SELECT statement, to a complex script with expressions, temp tables, control of flow, insert/update/delete, database modifications and so on. Reports are generally intended for read-only data retrieval query batches, but a valid batch is a valid batch, and you can get really creative with them.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Once your dataset queries become larger, more complex, interdependent, and re-used in more places, it becomes more and more necessary to apply CS principles and abstract the complexity from the dataset queries. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>SQL Server supports “Views” which are basically a packaged SELECT statement that can then be queried with SELECT as if it’s a table, and “Stored Procedures” that can contain any TSQL statements but must be called with the EXEC(UTE) statement. SQL also supports functions (static, dynamic, and table valued) and other programmatic constructs, but views and stored procedures usually fit my reporting needs.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>In my “hierarchical-reports/queries” example above, I create a set of hierarchic views that are used in hierarchic reports. I start with the lowest level detail query that contains the bulk of the join and filtering logic along with the rest of the columns that will be used for higher level groupings:</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>CREATE VIEW PersonBugsView as</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>SELECT g.GroupName, t.TeamName, p.PersonName, b.Priority,‘BugCount’=count(distinct b.BugId)</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>FROM Bugs b</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>JOIN Persons p on p.PersonId = b.PersonId</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>JOIN Teams t on t.TeamId = p.TeamId</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>JOIN Groups on g.GroupId = t.GroupId</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>WHERE b.Status = ‘Active’ and b.Type = ‘Code Defect’ and b.Release = ‘Current Release’</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>GROUP by g.GroupName, t.TeamName, p.PersonName</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Then I create the other hierarchical views on top of this detail view:</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>CREATE VIEW TeamBugsView as</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>SELECT GroupName, TeamName, Priority,‘BugCount’=sum(Bugcount)</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>FROM PersonBugsView</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>GROUP BY GroupName, TeamName, Priority</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>CREATE VIEW GroupBugsView as</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>SELECT GroupName, Priority, ‘BugCount’=sum(Bugcount)</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>FROM TeamBugsView</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>GROUP BY GroupName, Priority</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri>Once the view are created and tested to be correct (in SSMS</FONT><SPAN style="FONT-FAMILY: Wingdings; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings"><SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings">J</SPAN></SPAN><FONT face=Calibri>), I create the set of matching reports. The PersonBugsDetail report would have parameters for GroupName and TeamName, and the dataset query would be:</FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>SELECT PersonName, Priority , BugCount </FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>FROM PersonBugsView</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>WHERE GroupName = @GroupName and TeamName = @TeamName</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The TeamBugsSummary report would have a parameter for GroupName and the dataset query would be:</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>SELECT TeamName, Priority , BugCount</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>FROM TeamBugsView</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>WHERE GroupName = @GroupName</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>And the report table would have a drillthrough action on the BugCount textbox that calls the PersonBugsDetail report with the GroupName and TeamName parameter values.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>And finally, the GroupBugsSummary report would have a dataset that queries the GroupBugsView:</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>SELECT GroupName, Priority, BugCount</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>FROM GroupBugsView</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>And again here, the report table would have a drillthrough action on the BugCount textbox that calls the TeamBugsSummary report with the GroupName parameter value. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The value of views here is that you could reuse them across several different reports, and <I style="mso-bidi-font-style: normal">when</I> (not <I style="mso-bidi-font-style: normal">IF</I>) the query logic needs to be changed, you could just change the detail view, and the effects would automatically be applied to all of the views and reports without having to edit each. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri>There are a few pitfalls and counter-arguments to this approach. Abstracting the logic away from the report makes them more complex; many of my report plagiarizers like to steal my queries or look at the logic used in the report queries, and they don’t know where the view is stored or how to look at it, this can be a good or a bad thing</FONT><SPAN style="FONT-FAMILY: Wingdings; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings"><SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings">J</SPAN></SPAN><FONT face=Calibri>. Another argument is that you could communicate the same information in a single report on the detail query, and let the report table’s grouping expressions conditionally display the per-Group/Team information, and it will work fine for relatively small data sets. But if you’ve read my blog posting here </FONT></FONT><A href="https://blogs.msdn.com/deanka/archive/2009/01/13/pet-peeve-slow-reports.aspx" mce_href="https://blogs.msdn.com/deanka/archive/2009/01/13/pet-peeve-slow-reports.aspx"><FONT size=3 face=Calibri>https://blogs.msdn.com/deanka/archive/2009/01/13/pet-peeve-slow-reports.aspx</FONT></A><FONT size=3 face=Calibri>, large datasets along with complex grouping can result in slow running reports, and I hate that.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Another suggestion for dealing with complex dataset queries is to use stored procedures, they can be useful in many situations. For example, as I mentioned in the above blog posting, report queries can run for a long time if they join many tables and require “read” locks on several tables that are heavily used by other processes reading and updating the data. In this case, you can split the operations into stages, store the information from each stage in temp tables or table variables, and return the final data from these tables. And many reporting needs cannot be fulfilled by SELECT statements (from views or tables) alone.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri>I used this method for a set of daily reports used by my team to communicate the labrun verification needs (and ensure that Reporting Services is bug free</FONT><SPAN style="FONT-FAMILY: Wingdings; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-char-type: symbol; mso-symbol-font-family: Wingdings"><SPAN style="mso-char-type: symbol; mso-symbol-font-family: Wingdings">J</SPAN></SPAN><FONT face=Calibri>!) for our group. Our labrun automation database is used continuously for hundreds of testers and lab machines, and hundreds of thousands of tests. In addition to that, my manager requested that the report data be sorted by the 1-n priority of the labruns, not an easy task with SELECT or report expression logic alone.</FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>In this case, I had to split the query operations into chunks and store the data in temp tables, and write a cursor to loop through the data and assign a priority to each. The final TSQL batch was over 300 lines and had numerous temp tables, control of flow, date conversions and calculations and so on. I shudder at the thought of embedding this in each of the reports that need the same basic data.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Note that if you do use #temp tables in stored procedures used in reports, the query designers may give you an error similar to:</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT face=Calibri><FONT size=3>Msg 208, Level 16, State 0, Procedure TestProcedure, Line 3<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>Invalid object name '#temp'.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>I’ve discussed the issues and solutions in a forum posting here:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><SPAN style="FONT-FAMILY: 'Segoe UI','sans-serif'; COLOR: #333333"><A href="https://social.msdn.microsoft.com/Forums/en-US/sqlreportingservices/thread/4f62de2a-4efb-4cf2-8c84-cf0f62a99a11" mce_href="https://social.msdn.microsoft.com/Forums/en-US/sqlreportingservices/thread/4f62de2a-4efb-4cf2-8c84-cf0f62a99a11"><FONT size=3>https://social.msdn.microsoft.com/Forums/en-US/sqlreportingservices/thread/4f62de2a-4efb-4cf2-8c84-cf0f62a99a11</FONT></A><o:p></o:p></SPAN></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>but the net recommendation is to use table variables instead.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>And finally, remember to GRANT the necessary SELECT or EXECUTE permissions on the views and procedures created for your reports; I’ve spent a lot of time testing views, procedures, and reports locally only to find they fail miserably on the server when the report’s datasource used a different login from mine, and that login didn’t have permissions.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT size=3><FONT face=Calibri>Dynamic SQL Statements - EXEC()<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Many reporting situations require ad-hoc query type logic that can be difficult or inefficient with static dataset queries. If the logic only affects the filtering of the data in the WHERE clause of the query, you may be able to get away with using complex OR constructs, but the query will be inefficient. If you need to have SORTing specified dynamically at runtime, you may be able to work with interactive or expression-based sorting in the report definition, but these have their limitations, complexities, and inefficiencies as well. In these cases, dynamic SQL statements can be used.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The basic trick is to build up the SELECT statement as a text string based on parameter values, and execute it using the TSQL “EXEC()” statement. The one reporting specific requirement is that the final result set has to have the same set of column names and datatypes with each permutation of your dynamic query. The number of rows, the values in them, and the order can be dynamic, but the names and types must be consistent. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>For example, consider a situation where you want the data to be sorted by any combination of three columns, and you want the data to be filtered by any column name and value, all specified by the user at report run time. For a report based on the PersonBugsView mentioned above, you could define 3 report parameters for sorting and a set for filtering:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri></FONT></P><FONT size=3 face=Calibri>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><B><SPAN style="FONT-SIZE: 12pt; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-fareast-font-family: 'Times New Roman'; mso-bidi-font-family: Calibri">ParmName<SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Available values</SPAN></B><SPAN style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt; mso-fareast-font-family: 'Times New Roman'"><o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><SPAN style="FONT-SIZE: 12pt; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-fareast-font-family: 'Times New Roman'; mso-bidi-font-family: Calibri">SortCol1<SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>GroupName, TeamName, PersonName, Priority, BugCount</SPAN><SPAN style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt; mso-fareast-font-family: 'Times New Roman'"><o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><SPAN style="FONT-SIZE: 12pt; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-fareast-font-family: 'Times New Roman'; mso-bidi-font-family: Calibri">SortCol2 <SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>GroupName, TeamName, PersonName, Priority, BugCount</SPAN><SPAN style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt; mso-fareast-font-family: 'Times New Roman'"><o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><SPAN style="FONT-SIZE: 12pt; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-fareast-font-family: 'Times New Roman'; mso-bidi-font-family: Calibri">SortCol3 <SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>GroupName, TeamName, PersonName, Priority, BugCount</SPAN><SPAN style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt; mso-fareast-font-family: 'Times New Roman'"><o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><SPAN style="FONT-SIZE: 12pt; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-fareast-font-family: 'Times New Roman'; mso-bidi-font-family: Calibri">FilterColName<SPAN style="mso-tab-count: 1"> </SPAN>PersonName, Priority, BugCount</SPAN><SPAN style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt; mso-fareast-font-family: 'Times New Roman'"><o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><SPAN style="FONT-SIZE: 12pt; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-fareast-font-family: 'Times New Roman'; mso-bidi-font-family: Calibri">FilterOperator<SPAN style="mso-tab-count: 1"> </SPAN>&lt;,&gt;,&lt;=,&gt;=,&lt;&gt;</SPAN><SPAN style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt; mso-fareast-font-family: 'Times New Roman'"><o:p></o:p></SPAN></P>
<P style="LINE-HEIGHT: normal; MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><SPAN style="FONT-SIZE: 12pt; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-fareast-font-family: 'Times New Roman'; mso-bidi-font-family: Calibri">FilterValue<SPAN style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>(dynamic, based on the FiltercolName specified)</SPAN><SPAN style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt; mso-fareast-font-family: 'Times New Roman'"><o:p></o:p></SPAN></P></FONT>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>DECLARE @sql varchar(1000) – be sure it is big enough to hold the whole query!</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>SELECT @sql =</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>‘SELECT GroupName, TeamName, PersonName, Priority, BugCount</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>FROM PersonBugsView</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>WHERE ‘ + @FilterColName + ‘ ‘ + @ FilterOperator + ‘ ‘ + @ FilterValue + ‘</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>ORDER BY ‘ + @SortCol1 + ‘, ‘ + @SortCol2 + ‘, ’ + SortCol3</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>Print @sql – Note that this needs to be commented in the report but is good for debugging in SSMS</FONT></P>
<P style="MARGIN: 0in 0in 10pt 0.5in" class=MsoNormal><FONT size=3 face=Calibri>EXEC(@sql) – This will run the query, it better be syntactically correct!</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>There are a few very important considerations when using this technique, first and foremost is SQL Injection attacks; any time you dynamically build a query string based on user provided strings, you run the risk of a hacker figuring out a way to make the query do something far different (and worse) than you intended. There are a few mitigations to this attack; ie only allow available values you specify, not freetext strings, and ensuring that the login used by the report’s datasource only has limited necessary SELECT permissions in the database. But in any event, you should study up on this attack and understand the implications completely before using this technique. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Another consideration is that the dynamic statement must always be syntactically correct for all possible values specified by the users. In my code example above, the @FilterValue may need single quotes for strings and no quotes for numbers, and the statement would be invalid if any of the parameters send blank values to the expression. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>And again, be sure to code and test this in SSMS using the PRINT statement to display the built query so you can see what is being executed, and be sure to comment out the PRINT statement when you’re pasting it into the query designer.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><B style="mso-bidi-font-weight: normal"><FONT size=3><FONT face=Calibri>Using SQL Profiler to see what query is being sent to SQL Server<o:p></o:p></FONT></FONT></B></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>SQL Profiler is an <I style="mso-bidi-font-style: normal">extremely</I> valuable tool for reverse engineering what queries an application is sending to SQL Server, and this applies to the report designer tools and the Reporting Services server applications as well.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>SQL Profiler is somewhat of a “man in the middle” between the application and the SQL Server executing its queries. It works by registering a set of events you want to trace in SQL Server, and when those events happen, SQL Server returns information associated with the event. The key events in this context are “Stored Procedures-RPC Completed” and “TSQL-SQL:BatchCompleted”, and the event data returned for these is the SQL statement being sent by the application. In addition to events, you can also customize the columns of information returned by the events, and add filters to restrict the rows of data being returned. Fortunately when you start SQL Profiler (via the Tools menu option in SSMS), the default events, columns, and filters will be sufficient for your needs. If the report queries are run in a server with a lot of other query traffic, you may need to filter to just the login name used in the datasource login.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>There are a few key situations in reporting where this is most useful. At the top of the list is working with multi-value parameters. As I mentioned above, SQL Server needs to see a nice clean list of values that it can execute like “…WHERE colname IN (1,2,3)”, but depending on the strings in your multi-value parameter list, it can be difficult to build a syntactically correct list of values that SQL Server can understand. In this case, you can build a report with a multivalue parameter and a datasource query that uses it in a “…WHERE colname IN(@parmname)” fashion, if it works fine, GREAT! But if you get errors, it can be really tough to figure out what the report designer is doing with your parameters and expressions without SQL Profiler. But you can also use it for any data set query that is returning errors for reasons you don’t understand. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;I hope the helps, Thanks! - Dean</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri>As usual, "This blog is provided 'AS IS' with no warranties, and confers no rights."<o:p></o:p></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P></SPAN></o:p>
<P></SPAN></P>]]></content:encoded>
    </item>
    <item>
      <title>Improving Report Performance with Caching</title>
      <link>https://docs.microsoft.com/archive/blogs/deanka/improving-report-performance-with-caching</link>
      <pubDate>Sat, 14 Mar 2009 01:56:00 GMT</pubDate>
      <dc:creator><![CDATA[Dean Kalanquin-ExMSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/deanka/2009/03/14/improving-report-performance-with-caching/</guid>
      <description><![CDATA[Hey There – I've not blogged in a while, and as a Reporting Services admin I’ve recently taken an...]]></description>
      <content:encoded><![CDATA[<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Hey There – I've not blogged in a while, and as a Reporting Services admin I’ve recently taken an interest in report caching issues, so&nbsp;I thought I would share some of my learnings and recommendations. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>First of all, I’ll give a quick review of the factors you need to consider when making report caching decisions. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG><U>Considerations for Caching Reports</U></STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG>How long does the report take to run? </STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Caching a long running report will allow the report to be rendered faster and give a better experience for the user, but it will not improve the time for reports that are already fast for live executions. Long running reports usually also have a high processing cost for both the report server that is processing the report, as well as the data source server that is executing the report’s queries. Considering this will allow you to determine if a report should be cached. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG>How “fresh” does the report data need to be?</STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Some reports, for example, those that reflect information in a fixed prior date range, will always return the exact same data every time the report queries are executed. These are perfect candidates for caching. Some reports, for example those showing current “status” information are only valid with the most current data available. These are the worst candidates for caching. Most other reports fall somewhere in the middle; the value and accuracy of the report is directly related to the age of the data, some latency is ok, but beyond a certain point it really should be refreshed. Considering this will also allow you to determine if a report should be cached, as well as how long the report should stay in the cache.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG>How often is the report used, and how many people use it?</STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>If a report is used very frequently, the costs of caching the report will be justified, but if the report is not used very often, the cost of caching it will not have any return. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG>When do users commonly use the report?</STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Some reports are repeatedly used at predictable times of the day, week, month etc, for example, a morning report showing the activity for the previous day. Other reports see a flurry of activity at non-predictable times, for example, when your manager sends the group an email questioning the status of a large project. Considering this will allow you to determine how and when the report should be cached. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG>What parameter values are commonly used for the report?</STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>If it&nbsp;is a report without any parameters, or users commonly choose the same parameters every time the report is rendered, the cached version will be valid for most requests. But if users commonly specify parameters that are not the same as those used in cached versions of the report, the cached version will not be used, and the report’s queries will be executed live. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG>Will users want to go back and see old versions of the report created at various points in the past?</STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>This is common for daily, weekly, or monthly status reports where users want to see older historic versions of the report. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p></o:p><FONT size=3 face=Calibri>All of these options must be considered together to determine if the report should be cached and how it should be cached.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri><STRONG><U>Types of Report &nbsp;Caching</U></STRONG></FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>There are two main types of report caching. When you click on the "Properties" tab of a report in Report Manager and choose the “Execution” option on the left side of the page, you will be configuring “Execution snapshots” that will only be kept in the cache for a specified period of time. When you choose the “History” option, you will be configuring “History Snapshots” that will store several cached versions of the report that can be manually chosen from the “History” tab of Report Manager at some time in the future. History snapshots can also store the cached versions of Execution snapshots.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG><U>Execution Snapshot Configuration Options</U></STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>There are two primary options for configuring “Execution Snapshot” report caching. You will see that by default, the “Always run this report with the most recent data” and “Do not cache temporary copies of this report” options are chosen by default. This of course means that the report’s data set queries will be executed each time the report is rendered. You may be able to avoid the execution time experience for the report user, as well as the reducing the processing cost for the report server and the database server the report is based on, by caching the report. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The first option is controlling how the caching is triggered. You can have the report cached when a user renders the report and a compatible cached version is not available. This is good for reports that are commonly used at unpredictable times of the day/week/month. The first person to run the report will see the time delay for the report queries to be executed live, but it will be cached for later users, and when activity slows down again, the report server and data source servers will not pay the price of executing and caching the report. This is a good option if you want to cache several versions of the report with different parameter values. This is the method that will be used when you choose one of the “Cache a temporary copy of the report…” options in Report Manager. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>Or you can have the report server automatically cache a new version on a specified interval or schedule, so a reasonably fresh cached version is always available. This is the method that is used when you choose the “Render this report from a report execution snapshot” option in Report Manager. It is a good option if you know when and how often the report will probably be used, but if the report is not used, the cost of executing and caching the report will be unnecessarily spent for no benefit to report users. This option is only good if the report has no parameters, or all the parameters have defaults, because the report server cannot prepare a cached version of the report if it does not know what users will specify when they run the report. </FONT><FONT size=3 face=Calibri>One precaution to be made here is that you need to consider how long the report takes to execute live when you are configuring how often it should be triggered. I mentioned in my blog post about report performance a story about a subscription that fired every 15 minutes for a report that ran for over an hour. This resulted in several executions running in parallel and slamming the SQL Server box that was executing the query. The same situation can happen with report snapshots, and the obvious suggestion is to make sure you don't initiate frequent snapshots for long running reports, or you will make enemies in dba and report server admin circles!</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>The second option is choosing how long the report will stay in the cache. You can have the cached version expired and removed at set interval or a scheduled basis, depending on when the age of the data in the report becomes outdated. This can be specified by choosing one of the “Cache a temporary copy of the report…” options in Report Manager.</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri><STRONG><U>History Snapshot Configuration Options</U></STRONG>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>The options for history snapshots are exposed via the History option of the Properties tab in Report Manager. They are pretty straight forward and similar to execution snapshots in many aspected; they can be created on a scheduled basis or by active live requests from execution snapshots, and they can be automatically be cleaned up on a scheduled basis. There are a couple key differences; they are exposed and visible for viewing by </FONT></o:p><o:p><FONT size=3 face=Calibri>selecting them in the History tab of Report Manager, whereas execution snapshots are automatically used (or not) depending on if they exist or have compatible parameters. You can also manually generate them (assuming the parameters all have defaults) using the "New Snapshot" option on the History tab.</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri></FONT></o:p>&nbsp;</P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG><U>Report Design Considerations for Caching Reports</U></STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>There are a couple considerations to keep in mind for cached reports. The first issue is that it is important for users to know that they are seeing a cached and potentially outdated view of the report. You can accomplish this by adding a textbox near the top of the report with an expression similar to:</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>="Report Executed at: " + Now().ToShortTimeString</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>As I’ve mentioned several times above, the cached version of the report is only valid if the parameter values of the cached report are the same as the values chosen by the person running the report. For this reason, if you want to take advantage of report caching, you should ensure that the report parameters have defaults that will be commonly used by the report users. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG><U>Pre-loading the Execution Snapshot Cache</U></STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>One of the drawbacks of non-scheduled execution snapshots is that they are only cached after the report was run once, and the first person to run the live version of the report pays the price of building the cached version. One way to avoid this is to have the report executed via a subscription that fires on a scheduled basis, and its execution caches the report without a user having to pay the price. A static subscription can be used if there are no parameters, all the parameters have defaults, or a single set of parameters will fit the needs of most users. You can also use a data driven subscription with a query that returns multiple sets of parameters to cover the needs of various combinations of parameters. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG><U>Identifying Existing Reports that are Good Candidates for Caching</U></STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>As I’ve mentioned in another blog posting, and Robert Bruckner covers extensively in his blog, the report server catalog database has a view called ExecutionLog2 that has entries for every report execution. You can use information in this view to identify the reports that are frequently executed live along with the parameter values that are specified, and are good candidates for caching based on the caching considerations listed above. </FONT><FONT size=3 face=Calibri>I created a report to serve this purpose, and it is based on the query below. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal mce_keep="true">&nbsp;</P>
<BLOCKQUOTE>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>select top 1000</FONT></P></BLOCKQUOTE>
<BLOCKQUOTE>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>'ReportName'=reverse(substring(reverse(el.ReportPath),1,charindex('/',reverse(el.ReportPath))-1)),</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>el.ReportPath, 'LastModBy'=u.UserName, 'Parameters'=convert(varchar(max),el.Parameters),</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>'TotDurationMin'=Sum(datediff(ss,el.TimeStart, el.TimeEnd)/60.0),</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>'AvgDurationMin'=Avg(datediff(ss,el.TimeStart, el.TimeEnd)/60.0),</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>'MinDurationMin'=Min(datediff(ss,el.TimeStart, el.TimeEnd)/60.0),</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>'MaxDurationMin'=Max(datediff(ss,el.TimeStart, el.TimeEnd)/60.0),</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>'UserCount'=COUNT(distinct el.UserName),</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>'ExecCount'=COUNT(*)</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>from<SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ReportServer.dbo.ExecutionLog2 el</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>left outer join ReportServer.dbo.Catalog<SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>c<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>on c.Path = el.ReportPath</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>left outer join ReportServer.dbo.Users<SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>u<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>on u.UserId = c.ModifiedByID</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3><FONT face=Calibri>where<SPAN style="mso-spacerun: yes">&nbsp; </SPAN></FONT></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>-- report parameters</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>(@reportpath = 'ALL' or el.ReportPath = @reportpath)</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>and<SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>el.TimeStart<SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>&lt; DATEADD(dd,@daycount,GetDate())</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>and el.Source<SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>= 'Live' -- only show live executions, not cached executions</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>and<SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>el.ReportAction<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>= 'Render' –- only include rendering operations</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>and<SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>el.ReportPath<SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>!= 'Unknown' -- exclude reports that have been deleted after executing</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>and<SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>el.Status<SPAN style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>= 'rsSuccess' -- </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>group by el.ReportPath, u.UserName,<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>convert(varchar(max),el.Parameters)</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>-- Only show the report executions that happen frequently, report parameter</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>having COUNT(*) &gt; @minexeccount</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>order by Avg(datediff(ss,el.TimeStart, el.TimeEnd)/60.0) desc</FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>&nbsp;</FONT></o:p></P></BLOCKQUOTE>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>By putting interactive sorting on the report’s table columns, I can look for the longer running reports, those that are executed frequently with a given set of parameters and so on. And I added drillthrough links that allow me to easily filter to just the data for a given report, as well as a MAILTO: URL that lets me easily contact the person who owns the report. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri>You can also use a similar query to monitor cached report activity.&nbsp;Reports that are executed live will have an ExecutionLog2 entry with RequestType=Interactive or Subscription, Report Action=Render, and Source=Live. Those that are rendered from an execution snapshot will have Source=Snapshot, and those from a history snapshot will have Source=History. If you have created scheduled execution or history snapshots for expensive reports, but don't see anyone actually using them, you may want to consider canceling the snapshot. </FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><FONT size=3 face=Calibri><STRONG><U>Internal Scheduling and Storage of Report Snapshots</U></STRONG></FONT></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri><STRONG>Disclaimer</STRONG>: we do not officially support querying all of the Reporting Services tables mentioned below, the columns and information in them may change in the future.&nbsp;</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>In my blog post about <A href="https://blogs.msdn.com/deanka/archive/2009/01/13/diagnosing-and-troubleshooting-subscriptions.aspx" mce_href="https://blogs.msdn.com/deanka/archive/2009/01/13/diagnosing-and-troubleshooting-subscriptions.aspx">monitoring and troubleshooting subscriptions</A>, I described how the scheduling of subscriptions is controlled by a SQL Agent Job that places an entry in the Events table, and it is moved to the Notifications table when the report server is processing the subscription. The same scheduling and delivery process is used by the schedules that&nbsp;refresh execution and history snapshots, with the exception that snapshots are stored in the catalog database rather than being sent to a file or email address. You can use the same basic techniques for monitoring, diagnosing, and troubleshooting snapshots. </FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>I mentioned several times above that the benefits of caching does come with a cost. Part of this is the execution cost of the report's dataset queries in the datasource server and&nbsp;part of this is in the report processing cost in the report server. But it also has a processing cost of managing snapshot cache entries, and a&nbsp;storage cost for storing the cached version of the report in the report server catalog database. </FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>The report snapshot cached entries are stored in the Snapshot &nbsp;table with information regarding when the cached entry was created, when it will expire, the parameter hash values used to determine if the cached report is valid for a new user request and so on. </FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>The actual </FONT></o:p><o:p><FONT size=3 face=Calibri>cached reports are stored in the Chunkdata table, and large report snapshots can require significant storage. For example, the Chunkdata table in my report server has 258 cached snapshot entries, with about 85mb of total data in the Content column, a max size of 14mb, and an average size of 335kb. The server&nbsp;does not use&nbsp;snapshots particularly heavily, at least until I start asking people to use them:), but this can be a significant storage issue in the report server catalog database if you do cache a lot of large reports, so it is something worth montoring from time to time in your server. </FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>I hope this information helps, take care, thanks, happy reporting, and of course:</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>"This blog is provided 'AS IS' with no warranties, and confers no rights."</FONT></o:p></P>
<P style="MARGIN: 0in 0in 10pt" class=MsoNormal><o:p><FONT size=3 face=Calibri>Dean</FONT></o:p></P>]]></content:encoded>
    </item>
    <item>
      <title>Monitoring and Troubleshooting Subscriptions</title>
      <link>https://docs.microsoft.com/archive/blogs/deanka/monitoring-and-troubleshooting-subscriptions</link>
      <pubDate>Tue, 13 Jan 2009 00:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Dean Kalanquin-ExMSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/deanka/2009/01/13/monitoring-and-troubleshooting-subscriptions/</guid>
      <description><![CDATA[As the owner of several production reports, I have subscriptions that fire on a daily or weekly...]]></description>
      <content:encoded><![CDATA[<P>As the owner of several production reports, I have subscriptions that fire on a daily or weekly basis to remind people to close their bugs or verify their labruns. As usual, the password for the account I use in the report datasources&nbsp;will expire, and I invariably forget to update all of them and I'll get the "I've not received the report for days" email. As the owner of the Report Server, I also don't want my box to&nbsp;waste time and resources processing a bunch of invalid reports or subscriptions. </P>
<P>For these reasons, I want to monitor the Report Server and know what is not working in the reports and subscriptions. </P>
<P>Let me start by giving a high level view of how subscriptions are defined and processed, but first:</P>
<P><STRONG>Disclaimer</STRONG>: we do not officially support querying all of the Reporting Services tables mentioned below, the columns and information in them may change in the future.</P>
<P><STRONG>Note: T</STRONG>his information refers to the SQL 2008 catalog, this info will generally also apply to SQL 2005, but I've not verified it there yet. Also, unless otherwise specified, the tables mentioned below are in your Report Server catalog database with whatever name you gave it, and yes, you need permissions necessary to query them.</P>
<P>Ok, back to the fun stuff.&nbsp;</P>
<P><STRONG><U>&nbsp;When you create a subscription several things are added to the RS server:</U></STRONG></P>
<P>- A row is placed in the Subscriptions table identifying the name of the report, along with parameter settings, data driven query info and so on to process the subscription</P>
<P>- A row is placed in the Schedule and ReportSchedule tables with the timing of the subscription</P>
<P>- A SQL Server Agent job is created to control the scheduled execution of the report, and this is stored in the sysjobs and sysjobsteps of the MSDB database. The agent job name is a guid value that represents the ScheduleId (yes, in case you've not yet noticed,&nbsp;this makes your Agent Job list messy with a bunch of guids that make it hard to work with your non-subscription jobs, we <EM>are </EM>hearing you:). </P>
<P>This query&nbsp;shows how the subscription information is stored in the various related tables, along with the most useful information you will need to diagnose issues later:</P><FONT color=#0000ff size=2><FONT color=#0000ff size=2><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<BLOCKQUOTE>
<P>select</FONT></FONT><FONT size=2><FONT color=#000000> </FONT></P></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'SubnDesc'</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>Description</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'SubnOwner'</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> us</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>UserName</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'LastStatus'</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>LastStatus</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'LastRun'</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>LastRunTime</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'ReportPath'</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> c</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>Path</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'ReportModifiedBy'</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> uc</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>UserName</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'ScheduleId'</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> rs</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>ScheduleId</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'SubscriptionId'</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2><FONT color=#000000>SubscriptionID</FONT></P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>from</FONT></FONT><FONT color=#000000 size=2> ReportServer</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>dbo</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2><FONT color=#000000>Subscriptions s</FONT></P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT color=#000000 size=2> ReportServer</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>dbo</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>Catalog</FONT></FONT><FONT color=#000000 size=2> c </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT color=#000000 size=2> c</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>ItemID </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2><FONT color=#000000>Report_OID</FONT></P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT color=#000000 size=2> ReportServer</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>dbo</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>ReportSchedule rs </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT color=#000000 size=2> rs</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>SubscriptionID </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2><FONT color=#000000>SubscriptionID</FONT></P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT color=#000000 size=2> ReportServer</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>dbo</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>Users uc </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT color=#000000 size=2> uc</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>UserID </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> c</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2><FONT color=#000000>ModifiedByID</FONT></P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT color=#000000 size=2> ReportServer</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>dbo</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>Users us </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT color=#000000 size=2> us</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>UserID </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2><FONT color=#000000>OwnerId</FONT></P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT color=#000000 size=2> msdb</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>dbo</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>sysjobs j </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT color=#000000 size=2> j</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>name </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT color=#000000 size=2> </FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>CONVERT</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>nvarchar</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT color=#000000 size=2>128</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>),</FONT></FONT><FONT color=#000000 size=2>rs</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#000000 size=2>ScheduleId</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>)</P></BLOCKQUOTE></FONT></FONT></FONT></FONT>
<P>&nbsp;I use a similar query in a report with parameters to filter to certain reports, subscriptions, owners, and last_status values.</P>
<P mce_keep="true"><STRONG><U>When the subscription runs several things happen:</U></STRONG></P>
<P>- The SQL Server Agent job fires and puts a row in the Event table in the RS catalog with the settings necessary to process the subscription</P>
<P>- The RS server service has a limited number of threads (2 per CPU) that poll the Event table every few seconds looking for subscriptions to process</P>
<P>- When it finds an event, it puts a row in the Notifications table and starts processing the subscription, which includes executing the data driven subscription query (if present), processing the report, rendering it to the specified format (HTML, PDF, Excel etc), and delivering the final result to either an email address or a file share. Note that there will also be a row in the ExecutionLog table for the report execution.</P>
<P>This &nbsp;query shows the subscription processing information that you can use to diagnose problems:</P><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<BLOCKQUOTE>
<P>select</FONT></FONT><FONT size=2> </P></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'Report'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> c</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>Path</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT size=2> </P></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'Subscription'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>Description</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'SubscriptionOwner'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> uo</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>UserName</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'SubscriptionModBy'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> um</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>UserName</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'SubscriptionModDate'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>ModifiedDate</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'ProcessStart'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> </FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>dateadd</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>DATEDIFF</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getutcdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(),</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>()),</FONT></FONT><FONT size=2>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>ProcessStart</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>),</FONT></FONT><FONT size=2> </P></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'NotificationEntered'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> </FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>dateadd</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>DATEDIFF</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getutcdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(),</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>()),</FONT></FONT><FONT size=2>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>NotificationEntered</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>),</FONT></FONT><FONT size=2> </P></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'ProcessAfter'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> </FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>dateadd</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>DATEDIFF</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getutcdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(),</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>()),</FONT></FONT><FONT size=2>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>ProcessAfter</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>),</FONT></FONT><FONT size=2> </P>
<P>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>Attempt</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT size=2> </P></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'SubscriptionLastRunTime'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> </FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>dateadd</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>DATEDIFF</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getutcdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(),</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>()),</FONT></FONT><FONT size=2>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>SubscriptionLastRunTime</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>),</FONT></FONT><FONT size=2> </P>
<P>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>IsDataDriven</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT size=2> </P></FONT><FONT color=#ff0000 size=2><FONT color=#ff0000 size=2>
<P>'ProcessHeartbeat'</FONT></FONT><FONT size=2> </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> </FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>dateadd</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>DATEDIFF</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(</FONT></FONT><FONT size=2>hh</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getutcdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>(),</FONT></FONT><FONT color=#ff00ff size=2><FONT color=#ff00ff size=2>Getdate</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>()),</FONT></FONT><FONT size=2>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>ProcessHeartbeat</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>),</FONT></FONT><FONT size=2> </P>
<P>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>Version</FONT></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>,</P></FONT></FONT><FONT size=2>
<P>n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>SubscriptionID</P></FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>
<P>from</FONT></FONT><FONT size=2> Notifications n</P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT size=2> Subscriptions s </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT size=2> n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>SubscriptionID </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>SubscriptionID</P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT size=2> </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>Catalog</FONT></FONT><FONT size=2> c </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT size=2> c</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>ItemID </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> n</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>ReportID</P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT size=2> Users uo </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT size=2> uo</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>UserID </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>OwnerID</P></FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>
<P>join</FONT></FONT><FONT size=2> Users um </FONT><FONT color=#0000ff size=2><FONT color=#0000ff size=2>on</FONT></FONT><FONT size=2> um</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>UserID </FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>=</FONT></FONT><FONT size=2> s</FONT><FONT color=#808080 size=2><FONT color=#808080 size=2>.</FONT></FONT><FONT size=2>ModifiedByID</P></BLOCKQUOTE></FONT>
<P><STRONG><U>Common problems&nbsp;with subscription delivery:</U></STRONG>&nbsp;</P>
<P>If your subscription emails are not being delivered to Inboxes across the company, there are several different things that can cause this:</P>
<P><STRONG>- The SQL Server Agent job&nbsp;is not firing</STRONG></P>
<P>This could be because the service is not running due to a password expiration or some other cause, and RS does not see anything to process. In this case, get the ScheduleId guid from the above query, find the job with that guid as its name, and check the job history to make sure the job fired. You will also see that the Event and Notifications tables are empty.</P>
<P><STRONG>- RS is not processing the subscription events</STRONG></P>
<P>The main issue here is that the RS service event processing threads&nbsp;are not processing the events. The main cause here is that the event processing threads are all tied up, and not processing the events as fast as they arrive. You will see that the agent jobs are firing, but the Events and Notifications tables have a large number of entries. The number of threads is fixed, you cannot increase it, so you need to figure out what they are processing and why they are not firing or keeping up. You can get the subscription and report information, and run the report or the data driven query manually to see if they are healthy. One thing I noticed is that a person had scheduled a report execution snapshot to fire every half hour, but the report took over an hour to process, so at any given time, several instances of the event were running at the same time occupying all of the threads, and nothing else was getting executed. </P>
<P>You may also notice that everything is healthy, but there are common hotspot times in the day (for example, 8am or midnight) when a lot of subscriptions are set to fire at the same time, and they may take a long time to complete, so the subscription processing cannot keep up and falls behind. In this case, you may encourage people to run them at different times if possible, or you could consider adding a second reporting server configured as a two node NLB cluster with the other. In this case, both servers will be polling the events table in the RS catalog and processing the events in parallel. </P>
<P><STRONG>- SMTP is not delivering the emails</STRONG></P>
<P>&nbsp;This could be because the email address is bad, or there are problems with the SMTP configuration. I won't go into SMTP here, I'm a database guy, but RS needs it to be working to deliver the report. The Event and Notifications tables will be empty (RS has done its part), the LastStatus column of the Subscriptions table and the subscription status in Report Manager will show an error similar to "Failure sending mail: The user or group name 'domain\username' is not recognized. Mail will not be resent."</P>
<P><STRONG>- The report or subscription is invalid</STRONG></P>
<P>Again, this can usually be tested by running the report manually, but there are a myriad of causes that will also show in the subscription status. For example, the datasource may be invalid due to an expired password or an account for a person who has left the company, a subreport may have been removed or renamed, a server, database, or table used in the query may have been removed or renamed and so on. Statistically, this is the source of over 90+% of failed subscriptions I've seen.</P>
<P>So in summary, you can write reports based on the information in these catalog tables to monitor subscriptions, and take action before you get the "I've not received reports for days" emails, and encourage the report and subscription owners to keep their stuff clean and healthy and be good reporting citizens. </P>
<P>In case its not obvious, you may want to host these monitoring reports on a different server because if subscriptions are not firing, you may not get the subscription based monitoring report to tell you that subscriptions are broken!</P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">I hope this helps, please let me know!</SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">Thanks, Dean<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">As usual, "This blog is provided 'AS IS' with no warranties, and confers no rights."<o:p></o:p></SPAN></P>
<P mce_keep="true">&nbsp;</P>]]></content:encoded>
    </item>
    <item>
      <title>Pet Peeve: Slow Reports</title>
      <link>https://docs.microsoft.com/archive/blogs/deanka/pet-peeve-slow-reports</link>
      <pubDate>Mon, 12 Jan 2009 21:58:00 GMT</pubDate>
      <dc:creator><![CDATA[Dean Kalanquin-ExMSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/deanka/2009/01/12/pet-peeve-slow-reports/</guid>
      <description><![CDATA[So for my first technical blog posting, I'd like to take on one of the most common issues I...]]></description>
      <content:encoded><![CDATA[<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">So for my first technical blog posting, I'd like to take on one of the most common issues I encounter when helping people write reports and when I run them myself. In my experience with SQL Server I've heard numerous application developers claim that SQL Server is slow or doesn't scale, and typically my response is that poor designs and/or poorly written queries are slow, SQL Server's perf is largely dependent on this. I've observed the same when people claim RS is slow, and my response is mostly the same.&nbsp;<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">Bear in mind that report execution time is directly related to the query execution time, the amount of data returned by the dataset query, and the complexity of the&nbsp;grouping, sorting, filtering, and aggregations in the report definition.&nbsp;Usually I can review the report, query, table design, or indexes and improve them dramatically. More often than not, the report writer does not have the option of changing table designs or indexes as they may not have admin or dbo permissions, or if they do, design changes would require broader application changes. So working on the dataset query and the report definition is your best bet.<o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">To start, I usually extract the query from the report and run it outside the report to see what's going on, mainly, how long does it take to execute and how many rows are being returned. Often times, the report may take 20 minutes to run, and if the standalone query also takes 20 minutes, there is not much opportunity to address this in the report. I'll review the tables and indexes to make sure the queries are good from that perspective, or if the same results can be obtained more quickly with a different approach in the query. Application developers who work with the database regularly may also be able to help. <o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">Another source of perf problems is contention, locking, and blocking in the database if there are a lot of users or processing using the same table. The resource manager in SQL Server Management Studio, 'SELECT * from SYSPROCESS', or SP_WHO will all show how many active processes are running on the box, and if there are blocked processes or large WAITs on lock resources. If there is, you may be able to benefit from breaking up the query. <o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">For example, in our test automation application, there are typically hundreds of processes running concurrently in the database where the automation application is running tests, testers are verifying labruns, and overzealous report writers like me running expensive reports, and all of them are getting and holding locks on the tables throughout the life of the query. The application has one "hot" table that is used by practically every process, as well as 10 or so other tables that are commonly joined with the hot table in queries. I've found that if I get only the necessary information from the hot table with a well indexed query, load this into a #temp table, and then collect the remaining data from the other tables joined with the #temp table in separate queries, the overall data can be returned to the report much more quickly. Now my peers in the relational engine may cringe if they read this, because it is nearly always better to use SELECT only without #temp tables if possible, but when there are several tables and lock contention going on, this can help dramatically. <o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">One of my first big wins in my reporting career (before I joined RS) was when my manager at the time had a labrun status report that took about 30 minutes to run. I was very familiar with the database, so he asked me to take a look and see if I could make it better. When I ran the query outside the report, it ran in a few minutes, but it returned about 20,000 rows. When I looked at the report, I saw that it was only displaying around 100 rows to the user, and I knew I was on to something. I noticed that the data was being displayed in a group section based on several columns in the report's dataset query,&nbsp;and the fields had several =COUNTROWS() and =SUM(columnname) aggregate functions in the report expressions. I took the columns used in the report table's group definition and added them to a GROUP BY clause in the query along with the other non-aggregated columns, and added the COUNT(*) and SUM(columnname) expressions to the column list of the query, and it executed in about 20 seconds! Then I removed the group from the table in the report,&nbsp;pasted the new query in the report dataset, and displayed the&nbsp;result set columns in&nbsp;the detail section of the table. I'd just converted&nbsp;a 30 minute report to a 20 second report, SCORE!!!<o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">When Reporting Services was running the original report, it had to execute the query, cache the result set, build b-trees and&nbsp;calculate the aggregations, and finally, build and render the report.&nbsp;In the new version of the report much of the data processing was done by SQL Server; Reporting Services only had to execute the query, cache a much smaller dataset, and then build and render the final report, which by the way looked exactly like the original report. While we do everything possible to optimize Reporting Service's processing of data, SQL Server is optimized to this and can do it much more efficiently. <o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">There is one flaw with this approach;&nbsp;report users commonly like to see the detail behind aggregates, so you can return the full data set and display it in a detail section that is hidden by default and toggled at the group level if the user wants to see it. To address this need, I still use the approach above, but provide a drillthrough link on the aggregated number to another report that shows the detail for the aggregated group.</SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'"><o:p>Robert Bruckner covers some of this material in his blog post <A class="" href="https://blogs.msdn.com/robertbruckner/archive/2009/01/05/executionlog2-view.aspx" mce_href="https://blogs.msdn.com/robertbruckner/archive/2009/01/05/executionlog2-view.aspx">here</A>, but one thing I'd like to repeat is that you can also use the ExecutionLog2 view in the Report Server catalog to identify and isolate these two issues. It has columns to show how much time is being spent in executing the report's dataset query vs the time spent processing the data. If you are a report server administrator, you can monitor the long running reports and provide my suggestions above to the report owner without going through the process yourself.</o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">I hope this helps, please let me know!<o:p></o:p></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: 'Arial','sans-serif'">As usual, "This blog is provided 'AS IS' with no warranties, and confers no rights."<o:p></o:p></SPAN></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><o:p><FONT face=Calibri size=3>&nbsp;</FONT></o:p></P>
<P mce_keep="true">&nbsp;</P>]]></content:encoded>
    </item>
    <item>
      <title>Introducing Myself</title>
      <link>https://docs.microsoft.com/archive/blogs/deanka/introducing-myself</link>
      <pubDate>Thu, 08 Jan 2009 21:14:00 GMT</pubDate>
      <dc:creator><![CDATA[Dean Kalanquin-ExMSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/deanka/2009/01/08/introducing-myself/</guid>
      <description><![CDATA[Hey There - Welcome to my new blog, before I start posting, I'd like to tell you about myself so you...]]></description>
      <content:encoded><![CDATA[<P>Hey There - Welcome to my new blog, before I start posting, I'd like to tell you about myself so you know a bit about how I think and what I'd like to accomplish with this blog. </P>
<P>I'm a true local, born and raised and educated&nbsp;in the Seattle area. I have a bachelor's degree in Business Management and more or less picked up computer skills along the way. My first real job after college was at a company called Microrim that shipped the first truly relational database product called R:Base System V, which was PC Magazine's Product of the Year around 1988 or so. I've been at Microsoft since 1990, and my first role was supporting Windows 3.0 during its first release. Around 1993 I joined the SQL Server product group as a software test engineer, and was the ONLY tester for the SQL Server Engine and the TSQL programming langauge (it takes about 300 people to fill that role now:), and shipped SQL Server 4.2, 6.0, and 6.5. Then I was a technical writer (gag) during SQL 7.0 owning half of the TSQL Reference and conceptual material. Then a&nbsp;tester in the Replication team during the creation of the Merge Replication feature set for the 8.0 release, and was the test lead when I left. Following that I was a test manager for the SQL "Integration Testing" team, a test lead for the Execution Engine through the 9.0 release. Since then I've been a test lead in the Reporting Services team, and own the "Server and Management Tools" components that includes the RS Windows and Web services, the SOAP and WMI APIs, the RS catalog, setup/upgrade, Report Manager, Config tool, Sharepoint Integration, and several other misc components and features. </P>
<P>To the core I'm a database guy and love working with relational database application develeopment and implementation, but my passion for the last 4 years or so is creating reports with Reporting Services. I'm intimately familiar with all of the databases that support the SQL Business unit, primarily the applications for tracking bugs and managing the test automation, and have written hundreds of reports to help us run the business and ship great software. I also own the production Reporting Services server that supports the business unit, and spend a lot of time monitoring the server and helping others in the unit with their reporting needs. </P>
<P>I have many very talented peers who have much more depth in the core Reporting Services technologies, and also love to blog, so I'd like to take a slightly differently approach than them. What I hope to share with you in this blogs is what I've learned creating reports, using the other RS features, managing a production server, and playing with the catalog.</P>
<P>Please feel free to contact me and give me feedback on my blog, other questions you have about these areas, and any disagreements, corrections, or enhancements to my postings. </P>
<P>Take care, thanks, and of course "This blog is provided 'AS IS' with no warranties, and confers no rights."</P>
<P mce_keep="true">&nbsp;</P>]]></content:encoded>
    </item>
  </channel>
</rss>