Using runningtotal() inside a Max() in ssrs - reporting-services

I am building a report using a matrix which have 3 row group and 2 column group. Now I wanted to calculate the sum total of a column based on a row group. I searched for a solution but didn't got one.
The only solution that I could think of was to use a RunningValue Function
Now my running value column output is like
| Original Values || Running Value |
|-----------------||---------------|
| 6676 || 6777 |
|.................||---------------|
| 6859 || 13625 |
|.................||---------------|
| 5320 || 66830 |
Now I want the result 66830 so that i can use it to compute other values
so can anyone please tell me
How to use RunningTotal function inside MAX function
what I am doing is
=MAX(runningvalue(Fields!NET.Value,sum,"matrix1_MIX"))
but it gives me an error
An error occurred during local report processing.
The defination of the report'Report1' is invalid.
The Value expression for the textrun 'Textbox106.paragraphs[0]TextRuns[0]' contains an
aggregate function (or RunningVale or RowNumber functions) in the argument to another
aggregate functiion (or RunningValue). Aggregate functions cannot be nested inside other
aggregate functions.
Is there a workaround to this problem in ssrs.
Thanks in advance

The maximum of a RunningValue by sum is the same as the regular SUM of the same value, taken in the scope of the entire group. Try something like this:
=Sum(Fields!NET.Value, "matrix1_MIX")
Think carefully about what you use for the second argument, that's key to getting the figure you need.

Related

How to Combine Fields from Two Datasets SSRS?

I am working on a report to show the total number of hours an employee spent working. Our company tracks labor hours by Service Request and Work Order so I need to bring totals for each into the report.
I created two datasets- one for Work Orders and one for Service Requests. Ideally, I would like to combine the total number of Work Order hours with the total number of Service Request hours and present that number listed by employeeID since both datasets have the employeeID field.
I thought it would be as simple as:
=(SUM(Fields!TOTALHOURS_WO.Value, "DataSet1") + SUM(Fields!TOTALHOURS_SR.Value, "DataSet2"))
I don't get an error, however, I am getting a number which repeats for each employee so I know I'm doing something wrong.
Any help is greatly appreciated.
Mal,
As #StevenWhite mentioned, LOOKUP is probably the function you are looking for.
Here is an example for you. For the example datasets:
EmployeeID | TOTALHOURS_WO
-----------------------------------
123 | 12
456 | 3
EmployeeNum| TOTALHOURS_SR
-----------------------------------
123 | 2
456 | 5
You will note that each table in a SSRS report needs a DataSet assigned to it. I will assume your table is using our first DataSet, which we will name "DataSet1". The second dataset above will be "DataSet2".
For your total hours you will use an expression. It should look something like this:
=TOTALHOURS_WO + LOOKUP(Fields!EmployeeID.Value, Fields!EmployeeNum.Value, Fields!TOTALHOURS_SR.Value, "DataSet2")
So you will be adding the TOTALHOURS_WO from your local dataset to the result from the LOOKUP function. What lookup is doing is taking the first field from your local dataset, finding a match in the dataset provided to the function (as a string), and returning the field from the row it matched to. The last parameter is the dataset to search.
Just in case you get an error... it's always a good idea to cast data to the type you want to work with in case it comes in wrong. So...
=CINT(TOTALHOURS_WO) + CINT(LOOKUP(Fields!EmployeeID.Value, Fields!EmployeeNum.Value, Fields!TOTALHOURS_SR.Value, "DataSet2"))
This assumes you have a one to one match on employee ID. If you have to SUM both fields you can try this:
=SUM(CINT(TOTALHOURS_WO)) + SUM(LOOKUPSET(Fields!EmployeeID.Value, Fields!EmployeeNum.Value, CINT(Fields!TOTALHOURS_SR.Value), "DataSet2"))
SUM for TOTALHOURS_WO will give you the SUM in your current table group (so make sure you are grouping by staff ID in the table). It will then add it to the SUM of LOOKUPSET. LOOKUPSET works the same as lookup but returns an array of matches instead of the first.
Hope this helps.

Simple IIF statement to determine a sum

I am a rookie to SSRS and am having difficulty obtaining a Sum.
I want the cell to Sum the distinct values of "UnitNumber" which is what the report is using to generate each row of my table. The reasoning behind this is that behind the complex report, Unit numbers are distinct and provide me with distinct SqFt Values. For example:
Unit # | Sqft|
Unit 001 | 472 |
Unit 002 | 600 |
Unit 004 | 1203|
The below does not work:
Sum(IIF(Fields!Unitnumber.Value,1,Fields!SqFt.Value)
I either get "Contains an error: cannot be converted to String" or, #Error in the cell. I cannot solely use Sum(SqFt) because it dumps an aggregate of the whole dataset query (every single row summed up) Any ideas?
It seems that your IIF missing main condition. It can be something like below or do as per your requirement.
Sum(IIF(Fields!Unitnumber.Value=1,1,Fields!SqFt.Value)
Also, I would suggest you to make your expression like below,
=SUM(IIF(Fields!Unitnumber.Value = 'putyourcondition', Fields!SqFt.Value, 0))

Compare 2 dynamically created matrix columns to get a difference

I have a matrix set up with a Row Group called "company", a Column Group called "Year" and a value field that is a sum of charges through the year called revenue. The column Group will end up having 2 columns in it that are provided through user input. The end results is a matrix that will look something like this:
Year 1 | Year 2
Company: $500 $250
Company2: $750 $250
What I would like to do is add a column to the matrix that calculates the change from year 1 to year 2. Is there a way to do this within the matrix such as adding a new column with an expression that compares the 2 entries in the row or will I need to manipulate the SQL code to create a column that does this within the code? To that end here is a view of the code for the dataset if that is the way I need to go:
SELECT
company.cmp_id
,company.Company
,ChargeDetails.[Bill To ID]
,ChargeDetails.[Delivery Year]
,ChargeDetails.Revenue
FROM
ChargeDetails
LEFT OUTER JOIN company
ON ChargeDetails.[Bill To ID] = company.cmp_id
WHERE
ChargeDetails.[Delivery Year] = #DeliveryYear
OR
ChargeDetails.[Delivery Year] = #ComparisonYear2
ORDER BY ChargeDetails.[Delivery Year] DESC,ChargeDetails.Revenue DESC;`
Try to add a column outside the Year group in header type Change or what you want to put, in the below cell use this expression:
=Sum(iif(Max(Fields!DeliveryYear.Value)=Fields!DeliveryYear.Value,Fields!Revenue.Value,0))-
Sum(iif(Min(Fields!DeliveryYear.Value)=Fields!DeliveryYear.Value,Fields!Revenue.Value,0))
Note I am substracting the min year (1) revenue sum to the max
year (2) revenue sum.
In your example it will produce:
+-----------+--------+--------+--------+
| Company | Year 1 | Year 2 | Change |
+-----------+--------+--------+--------+
| Company A | 500 | 250 | -250 |
+-----------+--------+--------+--------+
You can define change in many ways maybe in percentage or any other measure, this is only an example, learn what the above expression is doing in order to calculate the measure you need.
EDIT 1:
Add the matrix with this data arrangement.
The matrix looks like this one in preview:
Be sure you added the Change column outside the DeliveryYear group as shown in above image. Also check the fields names correspond to yours in the expression.
EDIT 2:
If Revenue has null values you can try this:
Try replace the null values at query level using T-SQL ISNULL() Function, change your query in this part as follows
,ISNULL(ChargeDetails.Revenue,0)
However if DeliveryYear has null values you may want to ignore that records, so try to exclude it in the where clause.
Let me know if you need further help.
I ended up using the following as the expression in the change column.
=sum(iif(Fields!Delivery_Year.Value=Parameters!DeliveryYear.Value,1,0)*Fields!Revenue.Value) - sum(iif(Fields!Delivery_Year.Value=Parameters!ComparisonYear2.Value,1,0)* Fields!Revenue.Value)

RowNumber for group in SSRS 2005

I have a table in a SSRS report that is displaying only a group, not the table details. I want to find out the row number for the items that are being displayed so that I can use color banding. I tried using "Rowcount(Nothing)", but instead I get the row number of the detail table.
My underlying data is something like
ROwId Team Fan
1 Yankees John
2 Yankees Russ
3 Red Socks Mark
4 Red Socks Mary
...
8 Orioles Elliot
...
29 Dodgers Jim
...
43 Giants Harry
My table showing only the groups looks like this:
ROwId Team
2 Yankees
3 Red Socks
8 Orioles
29 Dodgers
43 Giants
I want it to look like
ROwId Team
1 Yankees
2 Red Socks
3 Orioles
4 Dodgers
5 Giants
You can do this with a RunningValue expression, something like:
=RunningValue(Fields!Team.Value, CountDistinct, "DataSet1")
DataSet1 being the name of the underlying dataset.
Consider the data:
Creating a simple report and comparing the RowNumber and RunningValue approaches shows that RunningValue gives your required results:
You can easily achieve this with a little bit of vbcode. Go to Report - Properties - code and type something like:
Dim rownumber = 0
Function writeRow()
rownumber = rownumber + 1
return rownumber
End Function
Then on your cell, call this function by using =Code.writeRow()
As soon as you start using groups inside the tables, the RowNumber and RunningGroup functions start getting some weird behaviours, thus it's easier to just write a bit of code to do what you want.
I am not convinced all suggestions above provide are a one for all solution. My scenario is I have a grouping that has has multiple columns. I could not use the agreed solution RunningValue because I don't have a single column to use in the function unless I combine (say a computed column) them all to make single unique column.
I could not use the VBA code function as is for the same reason and I had to use the same value across multiple columns and multiple properties for that matter unless I use some other kind of smarts where if I knew the number of uses (say N columns * M properties) then I could only update the RowNumber on every NxM calls however, I could not see any count columns function so if I added a column I would also need to increase my N constant. I also did not want to add a new column as also suggested to my grouping as I could not figure out how to hide it and I could not write a vba system where I could call function A that returns nothing but updates the value (i.e. called only once per group row) then call another function GetRowNumber which simply returns the rownumber variable because the colouring was done before the call so I always had one column out of sync to the rest.
My only other 2 solutions I could think of is put the combined column as mentioned earlier in the query itself or use DENSE_RANK and sort on all group columns, i.e.
DENSE_RANK() OVER (ORDER BY GroupCol1, GroupCol2, ...) AS RowNumber

Using "totals" functions on an already grouped query

I am having troubles using the aggregate functions when using features like group by in a query. Instead of my aggregate functions applying to the entire query recordset, they only apply to select groups determined by the nature of the query. For example:
Person Date Able
-----------------------------
A 21/05/13 0
B 21/05/13 -1
C 21/05/13 -1
D 21/05/13 0
(grouped by Person, Date, Able)
When applying aggregate functions:
Person Date Able Max(Able) Min(Date)
----------------------------------------------------
A 21/05/13 0 0 21/05/13
B 22/05/13 -1 -1 22/05/13
C 23/05/13 -1 -1 23/05/13
D 24/05/13 0 0 24/05/13
The aggregate functions are made entirely redundant unless the data is completely ungrouped. So far, I have been getting around it using:
1) Using another query to reference the initial query and determine the true aggregate values.
2) Have the form call this second query using the d functions (dlookup,dcount etc.)
In my particular scenario, I have a list (very similar to above) that needs to be presented in a certain order (ranked based on ID). However, I am using an expression in the query to define a different type of ranking. The idea is to show (using conditional formatting) the first record in this new rank. Illustrated below
Person Date ID CalculatedRank
--------------------------------------------
A 21/05/13 1 4
B 21/05/13 2 2
C 21/05/13 3 3
D 21/05/13 4 1
Ideally I would like to have another column that determines which one is first which could be easily acheived by:
first: [CalculatedRank] = Min( [CalculatedRank] )
But as described above, Min() is not giving me 1, it is giving me it on a per row basis (the minimimum isn't always 1 so I can't set this arbitrarily).
Right now I am using a separate query to reference this first query and I sort that based on the calculated rank. Conditional formatting can then use dlookup to determine whether it is first or not from the second query. However, everytime the form refreshes, or a requery is called, every single row's conditional formatting triggers another dlookup, which then references the first query recalculating the new rank, for every row!
As you can imagine, the delay is noticeable causing the cursor to be idle for >5seconds. I am not too sure about the internal mechanisms of access, but using the inbuilt debugger, a requery on a recordset of 4 rows caused my CalculateRank() function to be called 12 times, purely through the conditional formatting calling the second query.
In summary, I have pretty much narrowed it down to requiring a separate query (and therefore dlookup) to properly use the aggregate functions. If I was able to keep everything in one query, the conditional formatting wouldn't need to use dlookup on another query to determine its status.
I am sure I am not the only one that has had problems with this and was wondering if any solutions exist where I can avoid all the stacked querying.
As always, any help is much appreciated!
Wow, I see what you mean! For my table [Table1]
Person Date ID
------ ---------- --
A 2013-05-21 1
B 2013-05-21 2
C 2013-05-21 3
D 2013-05-21 4
and my query [qryTable1Ranked]
SELECT Table1.*, CalculateRank([ID]) AS CalculatedRank
FROM Table1;
which uses the following function in a standard VBA Module
Public Function CalculateRank(ID As Long) As Long
Dim r As Long
Select Case ID
Case 1
r = 4
Case 4
r = 1
Case Else
r = ID
End Select
CalculateRank = r
Debug.Print "x"
End Function
and returns
Person Date ID CalculatedRank
------ ---------- -- --------------
A 2013-05-21 1 4
B 2013-05-21 2 2
C 2013-05-21 3 3
D 2013-05-21 4 1
when I just double-click the query to open it in Datasheet View my ranking function gets called 4 times, once for each row.
If I create a continuous form based on that query and open the form my function gets called 4 times. Then if I add conditional formatting on the [CalculatedRank] text box using Value = DMin("CalculatedRank", "qryTable1Ranked") then my function gets called 32 times!
I found that I can cut that by half (to 16 times) if I add an invisible unbound textbox named [txtMinCalculatedRank], use the following code behind the form...
Option Compare Database
Option Explicit
Private Sub Form_Load()
UpdateMinCalculatedRank
End Sub
Private Sub UpdateMinCalculatedRank()
Me.txtMinCalculatedRank.Value = DMin("CalculatedRank", "qryTable1Ranked")
End Sub
...and change the Conditional Formatting rule to Value = [txtMinCalculatedRank].
I found that I could cut that by half again (to 8 times) if I changed the Record Source of the Form from qryTable1Ranked to Table1 (the base table) and changed the Control Source of the [CalculatedRank] text box to =CalculateRank([ID]) (still using the tricks from the previous tweak).
I think that's probably as good as it gets without going so far as to create a temporary table, or persisting the CalculatedRank (and perhaps an "IsMin" flag) in the base table.