I am currently trying to work on fixing some cascading parameter issues I am having.
We recently put in a fix to a few SSRS reports that help with rerunning cascading parameters when the parent parameter is changed, if it helps, here is the instructions we followed to implement this: Cascading Parameters.
The main structure for most of our reports works like this:
#Hierarchy, select whether you want to filter by "Market" or "Region"
#HierarchySelection, select which markets or regions you want to filter by
#PropertyInternal, gets used to refresh #Property automatically each time #HierarchySelection is changed, this is populated by a SQL dataset
#Property, select which properties you want within those given markets or regions, the defaults are set up as Parameters.PropertyInternal.Value and get refreshed each time #HierarchySelection is changed
In order to get #Property to refresh properly, we have to feed #PropertyInterval with a NEWID() so it sees it as a new value each time it runs, so the value is CONCAT( Property, "|", NEWID() )...Property is always a 4 digit character
The issue I am having is that now is that subscriptions are failing because the Property parameter gets refreshed and unselects the values. Or if I have to feed it values, I can't exactly feed the NEWID() as I am unaware of what the value will be when the report is run.
My only thought is to try and remove the GUID from #PropertyInternal and feed that value to #Property instead, but either that is not possible or I just cannot figure out the syntax.
Any one have any better ideas for how I am handling this or if there is a way to remove the GUID?
I am essentially looking for something similar to this if possible, but getting the error below it.
=SPLIT(LEFT(JOIN(Parameters!PropertyInternal.Value,","), 4),",")
The report parameter 'Property' has expression-based ValidValues. The sizes of the value and the label (multi-value) arrays have to be identical. (rsInvalidValidValueList)
Public Function CommaSeparatedParam(ByVal strArray As Object()) As String
Dim returnStr As String = String.Empty
For Each str As String In strArray
returnStr = returnStr + Mid(str, 1, InStr(str, "|") - 1) + ","
Next
Return Left(returnStr, Len(returnStr) - 1)
End Function
Related
I have an SSRS report with information about invoices my company has issued with things like amount, date, payment, amount due, etc. I'm using SSRS 2008 (not R2) in Visual Studio 2008 environment. My issue has to do with formatting the report using the Expression editor. Currently, an invoice will be formatted as silver if the invoice has an Amount Due (a column) over 0.01 (outstanding invoice). We also issue credits (negative amounts) and these are almost always the negative amount of a previous invoice.
So, an invoice that has a credit issued to it will still show as silver because it's amount due > 0.01. But if there is a credit issued to this invoice, it's not actually outstanding and should be white. For example, if an invoice is $100.00, and there is a credit after for ($100.00), the original invoice's background color should be switched to white.
Here's where the code explanation comes in. I thought this was possible with custom VB code in the report, but it seems like the Expression editor in SSRS will not recognize my function as it says "Unrecognized identifier". I googled this a bit, and most of the topics I came across said that it will show that but actually work anyway. Well, I'm pretty sure it's not working at all, because I put this in as my expression and got all white cells for a certain column:
=IIF(Fields!Amount_Due.Value > 0.01,
IIF(Code.HasCredit(Fields!Amount_Due.Value) = True, "Blue", "Silver"), "Red")
The HasCredit function is below.
Function HasCredit(ByVal currentAmt as Double) As Boolean
Dim i as Integer
Dim amt as Double
Dim amts as System.Collections.ArrayList = New System.Collections.ArrayList()
Dim negativeAmt as Double
Dim retValue as Boolean = "False"
i = 0
For i = 1 to Report.Parameters!Test.Count()
amt = Report.Parameters!Test.Value(i)
amts.Add(amt)
Next
negativeAmt = currentAmt * -1
If amts.Contains(negativeAmt) Then
retValue = "True"
End If
Return retValue
End Function
When these two pieces are run, I get white background for all cells of this column. I read something on the net saying that only Shared functions would work, but I found multiple other examples showing functions that were not shared that worked. When I make it shared, it gives me: BC30369 Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class. due to the Report.Parameters!Test.Count() line. I got the idea for using Report.Parameters from http://social.msdn.microsoft.com/Forums/en-US/sqlreportingservices/thread/a7d59224-0ee5-491e-883b-2e5fcb3edeab.
So to reiterate, what I'm basically trying to do is get the values of each cell in this column into a collection and, for any two amounts where one amount has a negative equivalent, give it a white background instead of silver.
In SSRS 2008 R2, even if you reference a valid custom code method, the expression editor may still warn that the identifier is invalid. This does not always mean that it's invalid. My function worked even though that warning was thrown.
After a bit more researching I figured out how to solve this. I basically had to add a textbox with the =Join() function so that all the values in a column were put in this textbox; then reference the textbox in custom code and use a boolean value in the expression editor. Detailed instructions below.
1) Add a multi-valued parameter to your report (Right click Parameters, Add Parameter). Name it, select Allow Multiple Values, select Hidden for parameter visibility. For Available Values tab, select Get values from a query. Point to your dataset, and set the Value field to the column you want the parameter to check. For me that was my Amount Due column. Label field is irrelevant and can be left blank. In the Default Values tab, do the same, make sure the value field is set to the same column as before. Under Advanced, select Never refresh.
2) Create a new textbox on your report. Might want to name it, ex txtColumnValues. Edit the expression and put this in: =Join(Parameters!YourParameter.Value, ",") This will get all of the field values from the column you specified in your parameter, each separated by a comma.
3) Edit your report's custom code and make a VB function (as Boolean) to check the textbox. For example here is my code.
Public Function HasCredit(Amt as Double, ri as ReportItems) as Boolean
Dim retValue as Boolean = False
If Amt > 0.00 AndAlso ri!txtAmounts.Value.Contains(Amt*-1) Then
retValue = True
End If
Return retValue
End Function
4) Finally go to your expression editor for the field you want to change. In my example I wanted to change the background color if the current field had a negative equivalent in one of the other fields (my textbox), so my code looked like this:
=IIF(Fields!Balance.Value > 0.01
AND Code.HasCredit(Fields!Balance.Value, ReportItems) = False, "Silver", "White")
I had to take separate pieces of info from 3 or 4 pages, put them together, and hope they worked... after about a week they did... I guess it's all about persistence. If you need any further help with this just let me know.
I have googled it a lot and found that usually it cannot be done. I came across one of the hacks here:
http://www.bp-msbi.com/2011/04/ssrs-cascading-parameters-refresh-solved/
But its not working for me in ssrs 2005. Just wondering if anyone else tried it in 2005.
Or is there any other hacks that can be tried.
As per this article the dependent parameter gets refreshed only when its values are invalidated by the selection in the first parameter. If we can invalidate the dependent parameter every time a parameter changes we will enforce a complete refresh. An easy way to do this is to attach a value such as a GUID obtained with the NEWID() T-SQL function.
So basically we want to introduce a fake parameter in between two real parameters. This fake parameter is supposed to return new values everytime as the storedproc behind it will add a guid to the resultset everytime that proc is called. So it forces complete refresh of the other parameters.
Now the main issue I am facing is :
Setting the default value of this fake parameter.
For the available values the storedproc behind the fake param runs and it returns data in the format say : result1,result2_GUIDFROMSQL
Now it looks like the same storedproc is called again to set the defult value if i ask it to get the default value from query. But as the storedproc is run again new guid comes and so the old value cannot be found so its not being set as desired.
I just need to figure out a mechanism to pass this guid from introduced param to the next param.
Thats where I am failing.
My issue can simply be replicated by creating a param whose datasource is this querystring.
select getdate() id, #name nid
So in this case how to set a default value for this param.
Below I'm going to present a detailed scenario and then I'll show the example implementation based on Visser and Abbi's answers.
Imagine that I have a report where each row is a project. A project has a Status column with values "In Progress" or "Complete" and a Project Manager column whose values are a person's name. Here are the rows in the table:
Project A (Status = In Progress, Project Manager = Bob)
Project B (Status = In Progress, Project Manager = Tom)
Project C (Status = Complete, Project Manager = Jack)
Project D (Status = Complete, Project Manager = Tom)
Project E (Status = Complete, Project Manager = Jill)
I want to have 2 parameters on my report
Show Completed Projects? - This is a boolean parameter
When false will only show "In Progress" projects A & B
When true will show "In Progress" projects A & B in addition to "Complete" projects C, D, & E.
Project Manager - This is a multi-value text parameter whose options and default values will need to change based on the Show Completed Projects? parameter upon which it is dependent.
If Show Completed Projects? is set to false then only "Bob" and "Tom" options will show up because they are the project managers for the in progress projects Project A & B respectively.
If Show Completed Projects? is set to true then in addition to "Bob" and "Tom" you will also have "Jack" and "Jill" show up as options because they are project managers for the inactive projects Project C & Project E respectively.
Now for the implementation:
Show Completed Projects? parameter
Project Managers dataset query (See Visser and Abbi's answers for details on how this generates a key that will change based on the independent parameter and will force SSRS to reload the default values)
SELECT
[ProjectManager_Key] =
pOuterAlias.[ProjectManager_Key] + '_' +
CAST(ROW_NUMBER() OVER(ORDER BY pOuterAlias.[ProjectManager_Key] DESC) AS NVARCHAR(MAX)),
[ProjectManager] = pOuterAlias.[ProjectManager]
FROM
(
SELECT
[ProjectManager_Key] =
pInnerAlias.ProjectManager + '_' +
CAST(ROW_NUMBER() OVER(ORDER BY pInnerAlias.ProjectManager ASC) AS NVARCHAR(MAX)),
[ProjectManager] = pInnerAlias.ProjectManager
FROM
(
SELECT
[ProjectManager]
FROM
[dbo].[Project]
WHERE
Status = 'In Progress' OR
#ShowCompletedProjects = 1
) pInnerAlias
) pOuterAlias
ORDER BY
pOuterAlias.[ProjectManager]
Project Manager parameter
General
Available Values
Default Values
Projects dataset
Query
SELECT
*
FROM
[dbo].[Project]
WHERE
(
Status = 'In Progress' OR
#ShowCompletedProjects = 1
) AND
Project Manager IN (#ProjectManager)
Parameters (Make sure to note the [#ProjectManager.Label] portion which will make it match the project on the actual project manager value from the database and not the key that we generated.
Finally I was able to resolve this. This link was a helpful start.
http://www.optimusbi.com/2012/07/16/multilevel-cascading-select/
Basically what it does is : Writing parameter query in a manner so that the dependent parameter changes its value every time you change its parent parameter.
The query adds rownumber with a "_" preceeding it. So every time user selects other values the rownumber changes and hence the query resultset.
Then when using the resultset remove the text after the underscore to get the real codes.
There is a workaround to fix this issue for all situations.
Note that the previous answer provided, writing parameter the query in a manner so that the dependent parameter changes its value every time you change its parent parameter, works for some cases but not all cases. If the dependent parameter's "Available" values change as a result of another parameter AND the dependent parameter is visible, it works, If the dependent parameter is hidden or internal, or if the "Available" values do not change as a result of the other parameter, it will not work.
The foolproof workaround is to define a custom function in the report and call it in an expression for the dependent parameter. The custom function takes the parameter's (the parameter that the dependent parameter depends upon) value as an argument, and simply returns its value. Even though function is simply taking the value and returning it, SSRS does not inspect the code and assumes that the code could do anything (generate a random number, pull files from disk, etc ...). So SSRS calls the function every single time the value changes regardless of whether the dependent parameter's "Available" values change, and regardless of whether the dependent parameter is visible, hidden, or internal.
Something like this:
public function returnArg(ByVal TheArg As String) As String
return TheArg
end function
Assume you have two parameters:
Parameter1
Parameter2
And that Parameter2 depends upon Parameter1.
Set Parameter2's value as an expression that includes the call to Parameter1 with the function, like:
=CODE.returnArg(Parameters!Parameter1.Value)
Now in this case Parameter2 simply displays the value in Parameter1, but this logic can be extended to more complex expressions and more than one parameter. As long as there is a CODE.returnArg(...) function call for each parameter in the expression, SSRS will always refresh the value.
#Abbi's second link is no longer valid. The article can now be found at:
http://www.optimusinfo.com/multilevel-cascading-select/
Unfortunately the images are broken and the article is a little incomplete without them. There is one image I was able to locate in its moved location (I could not find the others even with trying appropriate variations): http://www.optimusinfo.com/wp-content/uploads/2012/07/Multilevel-Cascading-with-Select-All-18.jpeg
This gave me the last clue I needed to get the technique working: when you're setting up the dataset parameter properties, you need to manually specify the parameter value as e.g. [#City.Label] rather than the usual plain old [#City] (which corresponds to [#City.Value].
This is because the technique alters the "Value" to a custom value which can no longer be looked up in the DB! So you need to use the user-friendly, db-existing "Label" as the parameter. (Probably should be obvious but...) The rest is all pretty standard if you understand report builder.
Also note that this link as it stands doesn't work with multi value cascading parameters. However it can easily be modified to do so: just alter the parameter stored procs to join to a value splitting function. I adapted mine from the udf_splitvarible function given at SQL Server - In clause with a declared variable
The modification is fairly simple but I'll give an example. This is from the article (with terrible formatting fixed).
SELECT
l11.CompanyName1+'_'+ CAST(row_number() over( order by l11.CompanyName1 desc) AS VARCHAR(50) )as CompanyName11
,l11.CompanyName
FROM
(
SELECT
l1.CompanyName+'_'+ CAST(row_number() over( order by l1.CompanyName asc) AS VARCHAR(50) )as CompanyName1
,l1.CompanyName
FROM
(
SELECT DISTINCT CompanyName
FROM Customers
WHERE City IN(#City)
)l1
)l11
ORDER BY l11.CompanyName ASC
Change:
SELECT DISTINCT CompanyName
FROM Customers
WHERE City IN(#City)
to:
SELECT DISTINCT CompanyName
FROM Customers
INNER JOIN udf_SplitVariable(#City,',') v ON City = v.Value
This all applies to SQL Server 2008/Report Builder 3 also.
I wasn't able to understand/apply #borkmark's answer. The limitations don't seem to apply to my case.
i have a report that i have been sending a parameter to which worked just fine.
the report parameter was declared:
string[] pclist = new string(){ "A", "B", "C" };
ReportParameter pcode = new ReportParameter( "pcode", pclist, false );
which gives me a new report parameter that is initialized with a string array of 3 values. so far so good.
inside the report, the 'pcode' parameter is defined as a 'String', and is used like:
...
and PCode in ( #pcode )
...
recently, because the values for the parameter needed to change depending on who is logged in, we changed this such that the parameter gets it's string array from the return value of a method call. (the method gets the values from the database):
ReportParameter pcode = new ReportParameter( "pcode", FetchParams("X"), false );
FetchParams("X") performs a select on the database and returns a string[] of values. most of the time it works fine. however, sometimes the report does not run and only returns the error message:
The 'pcode' parameter is missing a value
what we determined is that sometimes there are dozens of values be returned by FetchParams("X"). when the number of values in the string[] gets too big, the report fails with that error message. apparently, there is some kind of upper limit to the number of values that can be used to instantiate a ReportParameter object.
initially we thought that the limitation might be in sql server itself as a limit to the number of values that can be handled by an in clause. however, the error message does not seem to support this conclusion.
edit: trial and error has shown for this particular case, 33 is the upper limit for the number of values in the string array.
does anyone have any experience with this problem?
is there an upper limit to the number of values in the array passed to the ReportParameter ctor?
is there a way to configure the upper limit to be a larger number?
any and all help will be appreciated.
thanks!
hokay, folks, here is the real answer to this bit of mystery.
the report has a parameter called 'pcode'.
the 'pcode' parameter has a data source that creates a list of drop down values.
now the code that runs the report is generating a list of values for the pcode parameter.
when the list of values is passed to the report, if there is a value that doesn't match the report's list generated from it's data source, you get the mind-numbing ssrs error message:
The 'pcode' parameter is missing a value
ug-ly. but that's m$ for ya.
for the record, there does not seem to be a limit to the number of values in the string array being passed to the ReportParameter ctor... that and the error message is coming from ssrs, not the code generating the ReportParameter object.
hope this might help someone else that has this problem.
i am showing jobid in my report. i want that if job ID are this 02,07.11,19,21,29,31,40 etc then those report rows will not be shown. i know how to hide rows writing expression but i just need to know if there any short cut way to say that my job is are 02,07.11,19,21,29,31,40.
still i am doing like =IIF(Fields!JID.Value = 02 or Fields!JID.Value = 07 or Fields!JID.Value = 11 or Fields!JID.Value = 19, True, False)
is there any way like =IIF(Fields!JID.Value in (02,07.11,19,21,29,31,40), True, False)
if i send job id as a parameter value send from calling environment like "02,07.11,19,21,29,31,40" then how to di it.
please let me inform.
Will it work for your purposes to handle this in SQL? You have a couple options...
Option 1
Set up a multi-valued, string parameter in your report called #JIDValues.
SQL sprocs do not accept arrays of values, so you will need to pass this as a string array to your sproc. In order to do this, you will need to join your values together in your report code that executes the stored procedure.
JOIN(Parameters!SiteGroupList.Value, ",") & "'"
Inside the sproc (or the report depending on your preference) you will need to append and prepend commas to your string so that the following will work for your values on the ends as well as in the middle of the string. The following WHERE clause will be able to determine if a specific Job ID is contained in the comma delimited string you passed to the sproc.
WHERE #JIDValues LIKE '%,' + LTRIM(RTRIM(STR(tableA.JID))) + ',%'
Option 2
Alternatively, if this is a static list, you could hard-code your sproc to exclude them.
I am customizing an SSRS report, using an existing DataSource and DataSet. I can't edit the DataSet definition (no permissions and I'm not going to get it), so I have to work with the data provided. My problem is that some of the data is a bit garbled. I'm attempting to group columns on an "Iteration" text column, but it contains data like:
Iteration 01
...
Iteration 09
Iteration 1
Iteration 10
Iteration 11
....
Pending Scope
The "Iteration 1" field is in the middle and is really messing me up. How can I move this to the beginning of the set, or ideally merge it with Iteration 01? I can't do it in SQL, and it's not a trivial "number format", so I can't just apply a format to it.
How can group & display the data above with the numbers properly zero-padded on the left?
Many ways to do this, but which is best depends on the details of your incoming data. If the sample in the question is fairly complete then I would put some embedded code into the report:
Function ParsedTitle(Title As String) as String
Dim s as String
s = TRIM(Right(Title,2))
Dim num as Integer
If Len(Title) < 2 Then
return Title
End If
If Not Integer.TryParse(s, num) Then
return Title
End If
Return TRIM(LEFT(Title, Len(Title)-2) ) + " " + num.ToString("00")
End Function
(This was only lightly tested.)
Then set the expression in a field to be:
=Code.ParsedTitle(Fields!Title.Value)