Is there an issue with SPLIT(JOIN()) functions in SSRS 2012?
Here's why I ask...
I've just set up an SSRS 2012 server. I have an existing report I built in SSRS 2008 R2, which pulls from a 2005 database. I created a new project in MVS 2010 and added the existing rdl.
When I preview the report, the performance is at least 5 times worse than it is when I preview it in MVS 2008. I ran a trace and found that it took quite a while for SSRS to even execute the SP. Once it did, it rendered quickly.
I was trying to think of something that might slow down the SP's execution. The only thing I came up with is that I have a lot of multi-valued parameters I pass into the SP using SPLIT(JOIN()) functions. Have those been replaced by something new in 2012? If not, I don't even know where to start looking for the problem. My initial google searches have turned up nothing.
Has anyone experienced this problem or know of a list of things that worked well in 2008 but not so well in the new version?
I hope this question isn't too vague. Thanks for reading!
EDIT: I feel silly. I just traced the 2008 report execution and it turns out it does the same thing, which I never noticed before. The rendering is really quick after the SP shows up in Profiler. So...I have no clue what the problem could be. Any help would be GREATLY appreciated!
I would just stick with a predicate like:
Where thing in (#Sets)
Where the 'Sets' variable could be from another dataset I created obtained from SQL like:
Select 'Brett' as Name
Union
Select 'Anna'
Union
Select 'John'
Union
Select 'Jenny'
Simple choose to get the data for sets from 'get data from a dataset'. Once the variable is set SQL 2008R2 and higher should do the lifting for you with figuring out the clause of the predicate in expression of Where thing in (#Sets) actually translates to:
Where thing in ('Brett', 'Anna', 'John', 'Jenny')
The primary solution to speeding SSRS reports and decreasing server load is to cache the reports. If one does this (either my preloading the cache at 7:30 am for instance) or caches the reports on-hit, one will find massive gains in load speed.
Please note that I do this daily and professionally and am not simply waxing poetic on SSRS
Caching in SSRS
http://msdn.microsoft.com/en-us/library/ms155927.aspx
Pre-loading the Cache
http://msdn.microsoft.com/en-us/library/ms155876.aspx
If you do not like initial reports taking long and your data is static i.e. a daily general ledger or the like, meaning the data is relatively static over the day, you may increase the cache life-span.
Finally, you may also opt for business managers to instead receive these reports via email subscriptions, which will send them a point in time Excel report which they may find easier and more systematic.
You can also use parameters in SSRS to allow for easy parsing by the user and faster queries. In the query builder, type IN(#SSN) under the Filter column that you wish to parameterize, you will then find it created in the parameter folder just above data sources in the upper left of your BIDS GUI.
[If you do not see the data source section in SSRS, hit CTRL+ALT+D.
See a nearly identical question here: Performance Issuses with SSRS
Create a UDF which will take a comma (or some other delimiter you want to use) separated list, and return a table you can join on.
https://blogs.msdn.microsoft.com/amitjet/2009/12/11/convert-comma-separated-string-to-table-4-different-approaches/
Then you can take set up a parameter in your sproc such as #TheList varchar(max)
You should then be able to use it in a JOIN, Use it to create a temp table and then join on that in your query, or use it as a sub-select.
We use this quite often, and found that if you are primarily using values which are Integers, then the returned table should be a table of INT's to increase performance.
Pseudo example:
declare #TheList varchar(max)
set #TheList = ('1,2,3,4,5,6,7,8')
select *
from dbo.MyRecords r
join dbo.udf_CreateArrayTable(#TheList) at on r.RecID = at.RecID
Related
Is there a way to get the last run date from the cache refresh page for a SSRS report and have that displayed on the report? I'd like the user to know when the data was last refreshed.
You can query the ReportServer database directly to accomplish this:
SELECT MAX(els.TimeEnd) AS LastCacheRefresh
FROM dbo.ExecutionLogStorage AS els
INNER JOIN dbo.Catalog AS cat ON els.ReportID = cat.ItemID
WHERE els.RequestType = 2 --Refresh Cache
AND els.Status = 'rsSuccess'
AND cat.Name = 'MyReport'
Also FYI, Microsoft does not support querying the ReportServer database directly, which means fields/schema could change in later versions of SSRS.
Sorry to contribute to old thread, but wasn't finding a lot of hits on this question, so figured I'd add this:
If you include a calculated field in your source query, it is evaluated when the query is run and records along with the rest of your data. For example:
select
field1
, getdate() as QueryDateTime
from
SQLServerTable
and you can then use that value as min/max/first in your report (by definition is the same on every record).
This has to be done by the server dishing up the data, not as a calculated field in SSRS, because those are evaluated at run time, same as now() expression, or global execution time variable.
One downside of course is that you're recording that data and storing it, then having to retrieve a bunch of redundant data when pulling it, so it's not really efficient from a purist I/O perspective. I suspect the cost of one column of a single date value is not too much to worry about in most cases.
I'm trying to check our compliance with standards for documenting patient information, using SSRS 2008 R2. For example, in the group header for PatientID, I have =Not IsNothing(Fields!DATEOFBIRTH.Value). Now, I'd like to count the number of patients for which that returns True. The obvious way is
=Sum(Iif(Not IsNothing(Fields!DATEOFBIRTH.Value)
, 1
, 0
)
)
(which actually doesn't work, because I have multiple lines per patient, but never mind that for the moment.) The problem is that if I find my logic was wrong, I have to make the correction in two places, and it won't be obvious if I forget. In Crystal, I'd either use a running tally, evaluating on the change of group, or a manual tally with WhilePrintingRecords;, having a formula returning the T/F result in both locations. What's the generally-accepted SSRS way to do this? Thanks.
If you want to centralize logic, you can either use custom code in a report or create a custom assembly. With custom code, you'd have to update every report that uses that same logic if you find you need to make change, but it's simple to add to a report. With a custom assembly, you have to create a class library, compile it and add the code to your machine so BIDS can use it (and to every developer's machine if applicable) and to the report server. The advantage with the custom assembly is that there is one place to store the logic (not counting the distribution of the DLL) so every report gets updated automatically when you update the logic. This link is a starting point for you and provides links to more details on both of these options: http://msdn.microsoft.com/en-us/library/ms155798(v=sql.100).aspx.
I have a report working well where I extract the number of logins per user. Each login takes up one row on the report.
I have date parameters and my DB goes back a year. However it seems the report will only show 40/50 rows despite a report expecting to deliver, say, 250 for the amount of times I logged in.
Is there some setting in reporting services that limits the number of rows delivered. Can't find it anywhere..
Thanks.
The answer to your question: nope, as far as I know there's no real equivalent of SQL's TOP 50 statement in SSRS itself.
Some things that come to mind that may be causing your symptoms / can be investigated:
What happens if you run the query for the dataset in SSMS? Be sure to fill in the exact parameters the report's using (if any).
Run the query as a test from SSRS designer. If you're using Visual Studio: right-click the dataset and hit "Query...", then hit the red exclamation mark and fill in any parameters if needed.
Try putting a CountDistinct call (on your dataset) in a textbox somehwere in the report, by itself.
Check the filtering and grouping on your tablixes, perhaps even by looking at the XML source code for the RDL.
Show the parameters in textboxes (oldskool printf debugging! :D) to make sure they're what you expect them to be when the report's run on the Report Server. If they're not: try deleting the report on the server and re-deploying it.
Have a look at the ExecutionLog2 View in the ReportServer database, specifically the Number of Rows returned.
As mentioned in the comments by Atilla: You may also monitor exact SQL SSRS sends to server using SQL Server Profiler.
I'm tasked with reporting on survey data using Reporting Services 2008.
My challenge is this:
a survey has any number of questions
a question is one of three types (a numerical eval, a yes/no question, or a free text)
To handle this, I decided to use subreports on my main report, e.g. I defined one report for each of the three question types, and now when I'm reporting on a survey, I basically dynamically create a RDL for the survey report, using the three question types as subreports.
That actually works quite nicely so far - but I'm facing one major problem: how do I get the data into the subreports?
The approach I see right now is to have each (sub)report per question type define its own data set, based on a shared data source, to extract the values from the database. I'm pretty sure this would work - but I am not very keen on having potentially 5, 10, 20 subreports going off to the database to get their data independently.
What I was hoping for was being able to go fetch the data once for the whole survey, on the "main" report, and then just feed the appropriate subset of data into each subreport, as its being rendered - but I can't seem to find any way to do this....
Am I missing something totally obvious? I haven't had much exposure to Reporting Services, and my last project with it was four years ago (with Reporting Services 2000) - so there's a good chance I am just blind for the obvious solution :-) Please let me know!
Thanks for any hints, pointers to good articles or blogs on Reporting Services, and any help at all!
Marc
The usual way is to pass parameters (like date range) from main report to subreports and then subreports take care of everything else. To improve performance, see if you can render subreports from cache or snapshot. The cache stores report with combination of parameters passed, so after first "database hit" some or most subreports may actually be returned from the cache.
I struggled with the same issue. But there is a way you can achieve reasonable performance using "cached shared dataset". Basically subreports will use larger dataset including all rows for all subreports. By using "dataset filter" each subreport can filter out rows properly.
This is only available for 2008 version however.
If the parameters are the final data you require, then just use them and create a dummy dataset in the subreport - you could just have 'SELECT 1 AS DUMMY' as the sql (this is assuming that the subreports have distinct layouts from one another)
Or perhaps you can rethink the 'master' dataset with a function or table function?
It would still tax the sql server, but at least it would be doing it in one hit and the drain on the RS box would be less.
I'm using SQL Server Reporting Services and the report designer that comes with Visual Studio. I've got a really big report. It's actually so large that Visual Studio hangs (sometimes for hours at a time) or just crashes when I make changes.
There is preciously little I can do to solve the problem, so I've decided to just move the bottom half of the report into a sub-report. So, I started with one enormous, unresponsive report and ended with two small, manageable reports -- surprisingly, this actually works.
One problem: my subreport uses the same data as my main report. Right now, it populates its dataset by re-querying the database. The extra round-trip to the database causes the report to take twice as long to generate; up from 45 minutes to 1 1/2 hours to generate.
I'd like to avoid hitting the database again, and instead use the same dataset in both reports.
How can I share or pass a dataset between a report and subreport?
I think this can help you:
http://www.gotreportviewer.com/subreports/index.html
Supplying data for the subreport - the SubreportProcessing event To
supply data for the subreport you have to handle the
SubreportProcessing event. Note that this event is on the LocalReport
object. You can add an event handler like this:
private void MainForm_Load(object sender, EventArgs e)
{
this.reportViewer1.LocalReport.SubreportProcessing += new SubreportProcessingEventHandler(MySubreportEventHandler);
}
Below is an example for the event handler. In this example
LoadSalesData is defined to return a DataTable.
void MySubreportEventHandler(object sender, SubreportProcessingEventArgs e)
{
e.DataSources.Add(new ReportDataSource("Sales", LoadSalesData()));
}
If your report has multiple subreports you can look at the ReportPath
property of SubreportProcessingEventArgs and supply data for the
corresponding subreport. You may also want to examine the values of
Parameters property of SubreportProcessingEventArgs and only return
the subset of data that corresponds to the subreport parameters, as
mentioned here.
I'm pretty sure you can't. You're probably better off looking for ways to redesign the report entirely so that it's not so large... not to mention the various problems with subreports when exporting to excel.
I have several reports that the SQL is so complex in that it locks up Visual Studio when I try to edit it. In these reports I go straight into the Code view and edit the XML directly, which works. I also do this when Visual Studio mysteriously makes columns slightly wider than I set them at. However, I doubt you'd want to go down this path if you are editing the layout of the report too much.
Instead of running your query in the report, would it be possible to build a table using a stored procedure that both reports use? The first report runs the stored procedure to build the table and then both reports simply query the report. Watch for concurrency problems if the report can be run by multiple users.
Have you tried using a list within a list where both lists use the same dataset and then filter the inner list to display only records linked to the the outer list?
As far as the execution time, 45 minutes seems like an awful long time in the first place. I'm assuming you've done some analysis of the execution plan to verify your query or stored procedure is using meaningful indexes?
Hope this helps,
Bill
You can do it using a dummy parameter:
i. Create a parameter in your main report 'MyData' and tick 'internal'
ii. Set default value of 'MyData' to your data-set
iii. Set the sub-report parameter with the expression
=Parameters!MyData.Value
Hope this helps,
Duncan
If you create a table, you can merge all the cells of the details row and put a subreport as the contents. Then set the parameter of the subreport to the field you want to run the subreport against.
Jason