Expression to get a percentage - sql-server

I have a report which has a transaction type as a row group. There are two different types. I want to get the percentage of one type 2 compared to type 1.
I am not sure how to do this, I assume I need to use an expression which states the name of the transaction type and then make a calculation based on the other type.
So Instead of a total for July being 300, I would like the percentage of SOP+ compared to SOP-, so in this case 1.96%. For clarity, the figures in SOP+ are not treated as negative.

When you design a query to be used in a report, it is generally easier to work with different types of values being in separate columns. You can let the report do most of the grouping and aggregation for you. In that case, the expression would be something like this:
=Fields!SOP_PLUS.Value / Fields!SOP_MINUS.Value
Since they are both in rows in the same column, you have to use some logic to separate them out into columns and then do the operation.
You'll need to add two calculated fields to your dataset. Use an expression like this to get the values:
=IIf(Fields!TYPE_CODE.Value = "SOP+", Fields!SOP.Value, Nothing)
In other words, you will have new columns that have just the plus and minus values with blanks in the other rows. Now you can use a similar expression to earlier to compare them.
=Max(Fields!SOP_PLUS.Value) / Max(Fields!SOP_MINUS.Value)
Keep in mind that the Max function applies to the current group scope. When you add in multiple row and column groups to the mix this can get more complicated. If that becomes an issue, I would suggest looking at rewriting the query to provide these values in separate rows to make the report design easier.

WITH table1([sop-], [sop+]) AS (
SELECT 306, -6
UNION ALL
SELECT 606, -14)
SELECT(CAST([sop+] AS DECIMAL(5, 2)) / CAST([sop-] AS DECIMAL(5, 2))) * 100.0 FROM table1;
Returns :
-1.960784000
-2.310231000

Related

SSRS - Sum of an aggregated field

I have a report, that shows the individual sales commission for each employee.
The expression for the Sales Commission is:
=Sum(Fields!Total_Comission.Value)*Parameters!Distribution_Factor.Value*Fields!Individual_Factor.Value
Now I want to add a total for this field. If I just right click the cell and click on Add Total, it works but gives out the wrong total.
If I try to sum the field like this:
=Sum(Reportitems!GO_Prov.Value)
I get the error:
The Value expression for the textrun 'Textbox93.Paragraphs[0].TextRuns[0]' uses an aggregate function on a report item. Aggregate functions can be used only on report items contained in page headers and footers.
Is there a workaround to sum the aggregated field of this tablix? Maybe with a code?
Thanks in advance.
Update1:
Unfortunately i don't know how to write custom codes. But I found this code:
Public Total_lookup_Sum As Integer = 0
Public Function Lookup_Sum(ByVal value As Integer) As Integer
Total_lookup_Sum = Total_lookup_Sum + value
Return value
End Function
The expression i used for the Sales Commission is now:
=Code.Lookup_Sum((Sum(Fields!Total_Comission.Value)*Parameters!Distribution_Factor.Value*Fields!Individual_Factor.Value))
And the expression for the field where i would like to get the sum is:
=Code.Total_lookup_Sum
Now i get the error:
There is an error on line 0 of custom code: [BC30205] End of
statement expected.
Is there a way to solve this?
Scenarios like these can be tricky in SSRS. From your description (even though the screenshot doesn't really show everything), I'm guessing that you've got rows grouped by salesperson. In your column that's calculating the commission, you've got a sum of "Total_commission", but you've just got the "Individual_Factor" value not aggregated. Again, having a guess, but each underlying row (by employee) must have the same "Individual_Factor" value (so actually using Min(Individual_Factor) would give the same result).
But then, when you try and just take the same formula (or even a derivation of the formula), and make an overall aggregate of all of the rows, how does SSRS know which "Individual_Factor" value to use? You don't want Min() or Max(), because that would just be the lowest or highest value across all of the salespeople.
Your suggestion of a workaround via code is generally the way that I approach this. You need a report variable, something like "Commission_Grand_Total", and then you need a function in the report code that accepts 1 parameter, and in the function you'll add the parameter value to the variable. The easiest thing to do is to make the parameter the return value of the function.
Then, in the field where you currently have your commission formula (on the salesperson row), the expression in that field becomes =TheFunctionYouCreate((Sum(Fields!Total_Comission.Value)*Parameters!Distribution_Factor.Value*Fields!Individual_Factor.Value))
By passing the formula to the function, you're achieving two things:
The function will take each salesperson's calculated commission and add it to your report variable
The function will output the parameter value that you passed in (since you want to display the calculated commission amount on each salesperson's row)
Lastly, to display the overall total, the expression for that field is just the report variable that holds the overall total (that has been cumulatively added to as SSRS wrote out each salesperson's record)
TIP: I sometimes do this same sort of thing, but if I don't want the row-by-row value to be shown (I just want the cumulative total to be calculated), just put the expression that calls the function in a hidden column. SSRS will still run the function as it renders each row, but obviously it's just not displaying the result of the function.
Some MS reference for report variables and code
https://learn.microsoft.com/en-us/sql/reporting-services/report-design/built-in-collections-report-and-group-variables-references-report-builder?view=sql-server-ver15
https://learn.microsoft.com/en-us/sql/reporting-services/report-design/add-code-to-a-report-ssrs?view=sql-server-ver15
This is untested unfortunately but...
assuming you have a rowgroup for employee called EmployeeGroup then the expression would look like this...
=SUM(
Sum(Fields!Total_Comission.Value, "EmployeeGroup")
* Parameters!Distribution_Factor.Value
* FIRST(Fields!Individual_Factor.Value, "EmployeeGroup")
)
The inner expression reads as
Sum of Total_Comission with current employee group
... multiplied by Distribution Factor
... multiplied by the first individual factor with the current employee group
The outer expression just sums all the individual employee level sums.

Getting two separate values from one column

As mentioned above, I am currently trying to get two separate values derived from one column.
I have a column named statusDate, and I want to know whether if the status have been more than a month. If it's over a month, it will be categorized as Overdue, whereas, less than a month will be categorized as Pending.
This is what I have tried, however, it only gave me a value, either Pending or Overdue. However, I want both for comparison reason.
IF (DATEDIFF('month', today(), statusDate) > 1)
THEN 'OVERDUE'
ELSEIF (DATEDIFF('month', today(), statusDate) < 1)
THEN 'PENDING'
END
Is there anything wrong with my calculated field? Please help, thank you!
I have tried creating two different calculated fields and put both in the row section. However, the result was as following
I want to create a pie chart to show the percentage of Overdue(Long term) vs Pending(Short term).
However, I want both for comparison reason.
With your current setup you could easily compare the two values if you drag the calculated field on rows to get the values in separate rows or on colours if you want two differently coloured line graphs.
If you want to colour your pie chart for each state, use the pie as you have it and drag your calculated field to "Colour". This should split the pie as expected.
You could create two separate calculated fields as U.Y. Fried described although this doesn't really give you any additional possibility that you wouldn't already have with your current calculation.
In the majority of use cases it's a lot easier to compare values if they are in different rows (as you have it, separated by "overdue" and "pending") then comparing values that are in different columns.
Here is one way how to do it, You could create two fields by creating two calculated fields. the first you should call "overdue" use the following expression;
IF (DATEDIFF('month', today(), statusDate) > 1)
THEN 'OVERDUE'
END
for the "pending" use;
IF (DATEDIFF('month', today(), statusDate) < 1)
THEN 'PENDING'
END
IF you'd like to sum them up, return a number, for overdue use;
IF (DATEDIFF('month', today(), statusDate) > 1)
THEN 1
ELSE 0
END
do the opposite for pending.
Also you should take in consideration when it equals to exactly a month, use => for greater or equals, and =< for less or equals, it depends on your business logic.
Hope that this helps you!

Apply different formatting to different columns in a column group

I have some summary data, by year, that I am displaying in a tablix, in a pivot table fashion. The first column is the year, each row is a single year, and each column contains counts and dollar amounts from a query.
By default all of the value columns are formatted the same way. However, I need the first 4 columns formatted as a whole number (total counts) and the last 2 columns formatted as currency or 2 decimal places with commas. As shown here, my counts have .00 that I don't want shown.
Here is something that will get you going in the right direction:
=IIf(Fields!Type.Value = "Claims Filed" OR Fields!Type.Value = "Claims Approved", FormatNumber(Fields!Value.Value, 0), FormatCurrency(Fields!Value.Value, 2))
You will have to fill in for the other options, or switch these around to use the money values in the first part of the IIf since it is the shorter list. But, this should give you a good idea of how this can be done.
It is just a simple matter of conditional formatting, SSRS style.

inserting an artificial column in mdx query

from some reasons I need to insert an artificial(dummy) column into a mdx expression. (the reason is that i need to obtain a query with specific number of columns )
to ilustrate, this is my sample query:
SELECT {[Measures].[AFR],[Measures].[IB],[Measures].[IC All],[Measures].[IC_without_material],[Measures].[Nonconformance_PO],[Measures].[Nonconformance_GPT],[Measures].[PM_GPT_Weighted_Targets],[Measures].[PM_PO_Weighted_Targets], [Measures].[AVG_LC_Costs],[Measures].[AVG_MC_Costs]} ON COLUMNS,
([dim_ProductModel].[PLA].&[SME])
* ORDER( {([dim_ProductModel].[Warranty Group].children)} , ([Measures].[Nonconformance_GPT],[Dim_Date].[Date Full].&[2014-01-01]) ,desc)
* ([dim_ProductModel].[PLA Text].members - [dim_ProductModel].[PLA Text].[All])
* {[Dim_Date].[Date Full].&[2013-01-01]:[Dim_Date].[Date Full].&[2014-01-01]} ON ROWS
FROM [cub_dashboard_spares]
it is not very important, just some measures and crossjoined dimensions. Now I would need to add f.e. 2 extra columns, I don't care whether this would be a measure with null/0 values or another crossjoined dimension. Can I do this in some easy way without inserting any data into my cube?
In sql I can just write Select 0 or select "dummy1", but here it is not possible neither in ON ROWS nor in ON COLUMNS part of the query.
Thank you very much for your help,
Regards,
Peter
ps: so far I could just insert some measure more times, but I am interested whether there is a possibility to insert really "dummy" column
Your query just has the measures dimension on columns. The easiest way to extend it by some columns would be to repeat the last measure as many times that you get the correct number of columns.
Another possibility, which may be more efficient in case the last measure is complex to calculate would be to use
WITH member Measures.dummy as NULL
SELECT {[Measures].[AFR],[Measures].[IB],[Measures].[IC All],[Measures].[IC_without_material],[Measures].[Nonconformance_PO],[Measures].[Nonconformance_GPT],[Measures].[PM_GPT_Weighted_Targets],[Measures].[PM_PO_Weighted_Targets], [Measures].[AVG_LC_Costs],[Measures].[AVG_MC_Costs],
Measures.dummy, Measures.dummy, Measures.dummy
}
ON COLUMNS,
([dim_ProductModel].[PLA].&[SME])
* ORDER( {([dim_ProductModel].[Warranty Group].children)} , ([Measures].[Nonconformance_GPT],[Dim_Date].[Date Full].&[2014-01-01]) ,desc)
* ([dim_ProductModel].[PLA Text].members - [dim_ProductModel].[PLA Text].[All])
* {[Dim_Date].[Date Full].&[2013-01-01]:[Dim_Date].[Date Full].&[2014-01-01]}
ON ROWS
FROM [cub_dashboard_spares]
i. e. adding a dummy measure that should not need much computation as many times as you need it to the end of the columns.

Indexed view forcing non-optimal column type

We are trying to speed up some of our stored procs by reducing implicit conversions. One of the issues we are trying to figure out is how to fix several indexed views similar to this:
Time.UserID is INT
Time.TimeUnit is DECIMAL(9,2)
Time.BillingRate is MONEY
Select
UserID,
SUM(TimeUnit) as Hours,
SUM(TimeUnit*BillingRate) as BillableDollars
FROM
Time
GROUP BY
UserID
gives us a view with the columns:
UserID(int, null)
Hours(decimal(38,2), null)
BillableDollars(decimal(38,6), null)
We would prefer to have Hours(decimal(9,2),null) and BillableDollars(money,null).
CAST(SUM(TimeUnit*BillingRate) AS MONEY) as BillableDollars
Returned:
Cannot create the clustered index 'ix_indexName' on
view 'x.dbo.vw_viewName' because the
select list of the view contains an expression on result of aggregate
function or grouping column. Consider removing expression on result of
aggregate function or grouping column from select list.
And we were worried about the efficiency of SUM(CAST(TimeUnit*BillingRate AS MONEY)) as BillableDollars
What would be the best way to preserve these column types or is there a 'best practice'?
I might try adding a derived (actualized) "BillableDollars" column to the Time table with the conversion applied:
CONVERT(MONEY,(TimeUnit*BillingRate))
I've used money but, of course, the conversion could be to whatever data type most effectively meets your needs.
I believe this will allow you to have the indexed view while summing on the calculated billable dollars.
I expect that the reason you're getting a larger precision in the view datatype than the one in the table data type is that the sum of a bunch of numbers with a precision of 9 is adding up to a number that needs a precision greater than 9.
Just wrap your indexed view in second "casting" view like this
CREATE VIEW MyView
AS
SELECT CAST(UserID AS INT) AS UserID
, CAST(TimeUnit AS DECIMAL(9,2)) AS TimeUnit
, CAST(BillingRate AS MONEY) AS BillingRate
FROM MyViewIndexed WITH (NOEXPAND)
As a bonus you can include NOEXPAND hint so the underlying indexed view is actually utilized by query optimizer on less "advanced" editions of MSSQL.

Resources