Index was outside the bounds of the array in SSRS - reporting-services

I have two parameters , let's say P1 and P2. The sample expression I used for P2 is
IIF(P1.Label="string", "null" ,Split(P1.Label," ").GetValue(0))
When the condition is false, the split expression is working fine. But if the condition is true, I'm getting 'Index was outside the bounds of the array' error. If the condition is true, I need to pass the value "null" as varchar type.
Can someone please advice on this?

The problem with the IIF function is that it is a function not a language construct. This means that it evaluates both parameters before passing the parameters to the function. Consequently, if you are trying to do a Split on a parameter that can't be split, you will still get the 'Index was outside the bounds of the array' error, even when it looks like that code shouldn't be executed due to boolean condition of the IIF statement.
The best way to solve this is to create a safe string splitter function in custom code where you can use real language constructs. Also, check that the string is splittable by checking it contains a space instead of checking for a special string:
Public Function SafeSplit(ByVal SplitThis As String) As String
If InStr(SplitThis, " ") Then
Return Split(SplitThis, " ")(0)
End If
Return "null"
End Function
and then use this in your report for the Value expression instead of IIF:
=Code.SafeSplit(Parameters!P1.Label)

Related

How to handle empty values in multi parameter SSRS field

I'm trying to modify a report that uses a multi value parameter (a|b|c|d). That parameter is split into multiple columns. The expression on the first column is =Split(Fields!AlternateVendorDetails.Value.ToString(),"|")(0). If that field is blank in the query, the field in the column is blank, which is right. However, the other columns evaluate to an error (=Split(Fields!AlternateVendorDetails.Value.ToString(),"|")(1)). I've tried =IIF(Fields!AlternateVendorDetails.Value.ToString()='','',Split(Fields!AlternateVendorDetails.Value.ToString(),"|")(1)), and I'm not sure what else will work.
SSRS evaluates expressions even when using Iif conditions and that causes the error.
You need custom code to overcome the limitation.
You can create a function that takes the string, the delimiter and array item index, and returns the value if string is not empty and value index exists
Public Function GetArrayItem( s As String, d As String, i As Integer) As String
If s = "" Or Split(s, d).Length < i+1 Then
Return Nothing
Else
Return Split(s, d)(i)
End If
End Function
I ended up putting a COALESCE(value1 | value2 | value3,' | | ') in the query. That created the necessary fillers to set the values to blank.

Use Split, Join, and another function in SSRS

I have a field in SQL Server that contains an comma separated list. Here are 2 examples:
select 'ex1,ex2,ex3' as str union all
select 'ax1,ax2'
In my report, I have to transform all of these values (5 in this case) using a function. In this question I will use Trim, but in actuality we are using another custom made function with the same scope.
I know how I can split every value from the string and recombine them:
=Join(Split(Fields!str.Value,","),", ")
This works great. However, I need to execute a function before I recombine the values. I thought that this would work:
=Join( Trim(Split(Fields!VRN.Value,",")) ,", ")
However, this just gives me an error:
Value of type '1-dimensional array of String' cannot be converted to 'String'. (rsCompilerErrorInExpression)
I can't personally change the function that we use.
How do I use an extra function when dealing with both an split and a join?
You can use custom code to include all the logic (Split->Custom Code->Join).
Make adjustments inside the loop to call your custom function instead of trim
Public Function fixString (ByVal s As String) As String
Dim mystring() As String
mystring = s.Split(",")
For index As Integer = 0 To mystring.Length-1
mystring(index) = Trim(mystring(index))
Next
Return Join(mystring, ",")
End Function
To call the custom code use the following expression
Code.fixString( Fields!VRN.Value )

How do I call a function that's in a table, which is indexed by a string that I get from another function, and I also need to add arguments?

I have a table of functions that needs an argument and the functions are labelled with a string
local tbl = {fnct1 = function(x), fnct2 = function(x), ...}
That string do I get in a table that I returned from another function.
fnct1 = str[1]
These function should return a table.
Where do I put the argument when I call the function?
This doesn't seem to work:
table = tbl[str[1](x)]
As #lhf stated:
tbl[str[1]](x)
It might help to know that a function is a value.
Syntactically, you "acquire" any value through an expression. If you're acquiring a value that you think is a function, you can call it.
The syntax for a function call is the expression for the value followed by parentheses with 0 or more parameter expressions between.
So, tbl[str[1]] is an expression and the call is signified by ( and ) and x is the expression for one parameter. Both expressions are evaluated and the call is attempted. If the expression before the parentheses did not evaluate to a function value, you get an runtime error.
Breaking it down further, a table is a value that aggregates key-value pairs with unique keys. (The key is a value and the "value" is a value.) To obtain a "value" from a table, you index the table with the key. The prototypical syntax is: expression0 [ expression1 ], where expression0 should evaluate to a table value and expression1 should evaluate to the key value. You can apply that recursively to tbl[str[1]].
[As a convenience, if a key is a string that meets the rules for identifiers, you can index with the . operator (or, combined with a function call, the : operator).]
Where you usually put the arguments? After the function.
So, if function is tbl[str[1]], just add them right after as always: tbl[str[1]](args).

Is there a way to determine which optional arguments were given in a VBA function call?

Say I have a VBA function with an optional argument. Is there a way to tell, from within that function, whether the calling code has supplied the optional argument or not?
Public Function SomeFunction(optional argument as Integer = 0) As Integer
End Function
i.e. Is there a way to tell the difference between the following calls?
x = SomeFunction()
x = SomeFunction(0)
As far as I am aware it is not possible. If no argument is passed then the argument is initiliazed to its default value (0 in this case).
One way around this is to change the variable type to Variant and use the IsMissing function to check whether an argument is passed or not. Example:
Public Function SomeFunction(Optional argument As Variant) As Integer
If IsMissing(argument) Then
argument = 0
Else
// Code if argument not = 0....
End If
End Function
The IsMissing function works only with the Variant data type as any other data type will always have a default initialization value.
No, there is not.
The same problem exists in other language, such as C#, as expressed clearly in "C# In Depth", Chapter 13.1.1 (I read this part 15mins ago, it's normal I remember this!)
What I suggest you is not to set the default value in the function declaration. In the function, if argument is null, you set it to 0 and you know it wasn't supplied in the function call.
Edit : Just like #Remnant said in his answer, to be able to do this, the parameter type needs to be a variant.

SQL Reporting 2008; Check if an Array Contains a String

In SQL Reporting 2008 how can I determine if an Array Contains a String?
Example, I wish the following to return "1":
IIf(Split("a,b,c", ",").CONTAINS("a"), "1", "0")
What may be used in replace of the above CONTAINS function? Is it impossible? This value'd be the FilterExpression for my table. Its purpose is to decide what to show and what to hide.
If you are looking for an answer only in an expression, I am not positive. However, you can write .Net methods and call them just like expressions from a custom dll or a "code" section of the report. If you use built-in code, you can do something like the following:
http://www.vbforums.com/showthread.php?t=558440
Creating inline code or referencing an assembly in SSRS:
http://bryantlikes.com/pages/824.aspx
UPDATE:
Example to get your delimited values from your concatenated string:
http://www.dotnetperls.com/split-vbnet
UPDATE:
Here is a function you can use. You put it in the code section of the report:
Public Function Contains(ByVal ItemToCheck As String, ByVal CommaValuesList As String, ByVal delimeter As Char) As Boolean
Dim commaValues() As String = Split(CommaValuesList, delimeter, -1, CompareMethod.Text)
For Each commavalue As String In commaValues
If ItemToCheck.ToLower.Trim = commavalue.ToLower.Trim Then
Return True
End If
Next
Return False
End Function
Use the following syntax to reference it:
=code.Contains(param1,param2,param3)
Let's use MyLettersParameter as a multiselect parameter. To determine if it contains "a" use:
=Array.IndexOf(Parameters!MyLettersParameter.Value, "a") > -1
The above code returns true or false. To return "1" use:
=IIf(Array.IndexOf(Parameters!MyLettersParameter.Value, "a") > -1, "1", "0")