Calculate a Median in SSRS - reporting-services

We need to be able to calculate the median value of a set of figures for a statistical return - specifically the median Answered figures per contract for a date range.
The data is stored in a shared dataset for use in Report Builder, and this shared dataset is used a number of contractual reports so updating it is not an option. The shared dataset being used ensures consistency between contractual reports, so must be used.
There are answers to this already (e.g. Find the median of a calculated field in SSRS 2012 & Use of 'median' function in calculated field in SSRS) but these require either hidden rows/columns or using a calculated field in a graph.
We need an answer that allows us to use shared datasets/stored procedures and calculate the median value in SSRS/Report Builder.

This custom code can be added to the report:
Public Shared Function Median(ByVal items As Object()) As Decimal
If items Is Nothing Then
Return Nothing
End If
Dim counter As Integer = items.Length
If counter = 0 Then
Return 0
End If
System.Array.Sort(items)
If counter Mod 2 = 1 Then
Return items(CInt((counter / 2) - 0.5))
Else
Dim FirstIndex As Integer = counter \ 2
Dim SecondIndex As Integer = FirstIndex - 1
Dim FirstValue As Integer = items(FirstIndex)
Dim SecondValue As Integer = items(SecondIndex)
Return (FirstValue + SecondValue) / 2
End If
End Function
Which can then be called by using the following =Code.Median(Lookupset(Fields!Contract.Value, Fields!Contract.Value, Fields!Answered.Value, "DS_CallData_LKP"))
In this example the dataset "DS_CallData_LKP" is powering the entire report, but is being referenced back again to get list of values to be sorted for the median. Using a lookupset() instead of the hidden rows/columns method that is seen a lot helps keep the report simple for editing later down the line.

Related

Obtain list of occurring field values in report footer - ssrs

I'm trying to obtain a list of the different field values within an SSRS Report, and then display the result set on the page header.
See link below for an example.
I know that usually this would be completed simply with groups but the user does not want the Tasks split out.
Thanks
Example Report:
To get the unique values into a single cell, you will need to utilize the code behind functionality in SSRS.
Click on the background of your report and go to report properties -> Code. Paste this function into the window:
Public Shared Function RemoveDuplicates(ByVal items As Object()) As String()
System.Array.Sort(items)
Dim k As Integer = 0
For i As Integer = 0 To items.Length - 1
If i > 0 AndAlso items(i).Equals(items(i - 1)) Then
Continue For
End If
items(k) = items(i)
k += 1
Next
Dim unique As String = New [String](k - 1) {}
System.Array.Copy(items, 0, unique, 0, k)
Return unique
End Function
In the expression in your table where you want the unique values list, insert this expression:
=Join(Code.RemoveDuplicates(LookupSet(1,1, Fields!ID.Value, "DataSet1")), ",")
"Fields!ID.Value" is a reference to the field in your dataset that you want unique values from.
"DataSet1" is the name of the dataset that your field is located in.
What happens is that at runtime, the entire set of values from your query column are passed to the VB function which uses returns an array of unique values. The expression in the report Joins the unique array values back together with a comma deliminator.

excel vba: recordset joining and performance

The Context
I have an app in excel VBA for making read-only queries on a remote database.
Queries are executed from UDF's. My app passes an array of data from the recordset object to the function and Excel's fast process for writing an array to a cell range is invoked.
The Challenge
The app must be able to optionally return field names at the top of the dataset. This is presenting a huge performance challenge for me. The only way I know of to append or prepend to a 2D array in VBA is to loop through the entire array. Normally, I'm spared such a loop by passing the recordset.getRows() object directly to my UDF. However, when combining the list of fields and the result of the query with the looping method (the only method I'm aware of) I double or triple my calculation time for sizable queries.
I benchmarked this: for a query of 2k rows and 5 fields, average calc time without field names included is 4.3 seconds, vs. 9.8 seconds with field names
My first try was to combine the field names and recordset on the server using a UNION clause in my select statement (my server is MySQL). This does not work, however, since UNION forces data-type equality, implicitly converting my numerical data to strings. To convert them back I'd have to loop through the array, negating any efficiency gained.
My Question
Is there any object method of the recordset object or of VBA arrays that could be called upon to prepend a row to a large array without looping through the entire large array? The field names are all known before the MySQL query is executed.
My loop for joining the arrays is below. Define a new array arr of length of the recordset + 1, then loop through it, first adding the fields, then each row of the recordset array:
For r = LBound(arr, 1) To UBound(arr, 1)
If r = LBound(arr, 1) Then
arr(r) = fieldArray
Else
arr(r) = Application.Index(rs_array, r - 1, 0)
End If
Next
Using Application.Index is possibly the slowest way to combine your arrays: use a regular nested loop instead and you won't even notice any hit -
Sub TT()
Dim a(1 To 2000, 1 To 10)
Dim b(1 To 2000, 1 To 10)
Dim cc(1 To 2000)
Dim r, c, t
t = Timer
For r = 1 To 2000
For c = 1 To 10
b(r, c) = a(r, c)
Next c
Next r
Debug.Print "Loop", Timer - t '>> 0.015625 sec
t = Timer
For r = 1 To 2000
cc(r) = Application.Index(a, r, 0)
Next r
Debug.Print "Index", Timer - t '>> 4.195313 sec
End Sub

SSRS 2008r2 Using Aggregate on field with an Expression

SSRS 2008r2
I'm trying to perform an aggregate (Sum) on a field within a GROUPing that contains an expression.
The field where I want to SUM to appear is within a different GROUPing.
I've created the following Function within Report Properties
Dim public tot_OT_Hrs As Decimal
Public Function Add_OT_Hrs(ByVal OT_Hrs As Decimal) AS Decimal
tot_OT_Hrs = tot_OT_Hrs + OT_Hrs
return OT_Hrs
End Function
Public Function GetTotal()
return tot_OT_Hrs
End Function
I've added a call to the "Add_OT_Hrs" function in the field where the expression is and this works fine.
=Code.Add_OT_Hrs(
IIF(Sum(Fields!HrsWorked.Value) > Parameters!StdWorkingHrs.Value,
Sum(Fields!HrsWorked.Value) - Parameters!StdWorkingHrs.Value + Sum(Fields!Rate1Hrs.Value)
, Sum(Fields!Rate1Hrs.Value )
)
)
Fields!Rate1Hrs.Value is the field in which the expression resides in and Fields!HrsWorked.Value is an adjacent field
However, the field where I want to total to appear I've added the following
=Code.GetTotal()
All that is returned here is 0 (zero) on every row in the GROUPing. If I initialise the Dim public tot_OT_Hrs As Decimal variable to say 1.2 then 1.2 is returned on every row in the GROUPing. The Add_OT_Hrs function isn't working as expected.
Where am I going wrong?
Thanks in advance.
Looks to me that you are converting in the wrong order and also to the wrong data type. In the code example that you used you were converting the sum to a double which is not the correct data type. There is a notable difference between a double and decimal when using custom functions.
I tested this with a very small dataset as follows
select '1.2'
union all
select '1.8'
Then applied the following custom code
Dim public total as decimal
Public Function AddTotalr(ByVal r AS decimal) AS decimal
total = total + r
return r
End Function
Public Function GetTotalr()
return total
End Function
The order of your explicit conversion is vital. You have to convert the value before you sum the total so that you are working with consistent data types.
=code.addtotalr(sum(cdec(Fields!ID.Value)))
Even with the correct data type the this will produce an error because you are attempting to sum what is, most likely, being interpreted as a character string.
=code.addtotalr(cdec(sum(Fields!ID.Value)))
-----------edit ------------------------
As a test to see what the return on the function is change your function to
Dim public total as decimal
Public Function AddTotalr(ByVal r AS decimal) AS decimal
total = total + r
return total
This should give you a running total of the items processed and the last value in the list should match what your total should be.
I decided to do this in the proc.
It works out quite well now.

SSRS Report Unable to get withing scope of the values in RowGroup. Only got values using Previous and current scope value

I already calculated a row(Add Total row) like Gross profit within Group1(a,b,c,d,e) using Previous and current scope column group values. ex: Gross Profit = a - b.
Gross Profit = Previous(Sum(Fields!Trans_amount.Value)) - Sum(Fields!Trans_amount.Value) in Group1
Now, I want to get values for Net income like Net income = a - b - c in Group1. (OR)
Net income = Gross Profit - c in Group1 (Using Gross Profit textbox values using Reportitems!textbox.value). but, values different. Since Expression got new calculation)
Please help me.
Thanks Advance.
I think you want to get a "running value decreasing total" value in your report. For example, it returns "a-b" first, then "a-b-c", "a-b-c-d", "a-b-c-d-e". Is my understanding correct? In Reporting Services, it doesn't have any built-in function to achieve this goal. I make it by using custom code.
Public Shared Status as String
Public Shared Value as Integer
Public Shared Function GetValue(Item as Integer) as Integer
if Status is nothing then
value=item
status="1"
else
value= value - Item
end if
return Item
End Function
Public Shared Function GetTotal()
return value
End Function
Then call the function in Gross Profit cell:
=Code.GetValue(Sum(Fields!Trans_amount.Value))
To get the Net income, Call the function next to Gross Profit:
=Code.GetTotal()

Using a User-Created Function in VBA to output criteria in access query

In short: I have a user-created function (gettargetTemp(targetTemp_input) with one input that, upon function evaluation, I would like to be able to call the function in an Access query (design view) criteria field and have it represent the criteria string that I want evaluated when the query is run.
i.e. targetTemp_input = 1450 - this value is assigned from a form (and can vary)
Access Query
Field: Pad Temp
Criteria: gettargetTemp("targetTemp_input")
Criteria possibilities:
- Records within 100 degrees of the targetTemp_input value which
typically in Access query design is: Between 1350 and 1550 Or Is Null
- All of the records
The code that makes sense to me is:
Public Function gettargetTemp(targetTemp_input)
If Forms![Parameter Confirmation].tempCheck = True Then
gettargetTemp = "Between " & (targetTemp_input - 100) & " AND " _
& (targetTemp_input + 100)
Else
End If
End Function
The outcome of this typically results in a data mismatch error from Access. Is there a way to accomplish what I'm trying to do? Or maybe do it better? I'm not a programmer, but I have a pretty good technical background.
At present, you're trying to have the VBA function form an expression that does the wanted test, rather than doing the test itself. You need to change that:
Function IsTempOK(Temp) As Boolean
Dim ParamForm As Access.Form, TargetTemp As Long
Set ParamForm = Forms![Parameter Confirmation]
If ParamForm.tempCheck Then
If IsNull(Temp) Then
IsTempOK = True ' or False, if that's what you want
Else
TargetTemp = ParamForm.TargetTemp ' or wherever this is defined
IsTempOK = (Temp >= TargetTemp - 100) And (Temp <= TargetTemp + 100)
End If
Else
IsTempOK = True
End If
End Function
In the query definition, the WHERE clause should now use IsTempOK, passing the Temp field as the parameter.