Power BI Report Builder Indicator Formula - sql

I am adding in an indicator to a PBI Report Builder Report. The indicator is based off multiple fields from the dataset so I need to use a formula, to create the three up/down/side arrows.
Previously in Crystal Reports this could be implemented using a series of IF statements as follows. The below example is what is required for the down arrow. (the other 2 arrows also have multiple calculations)
IF (({spScorecard_SLView;1.CATEGORY_ID} = 4) OR
({spScorecard_SLView;1.CATEGORY_ID} = 25)) THEN
IF ({spScorecard_SLView;1.PM_3MM_NC_CNT}-{spScorecard_SLView;1.3MM_NC_CNT}) <
0 THEN 'Down Arrow'
ELSE IF (({spScorecard_SLView;1.CATEGORY_ID} = 21)
OR({spScorecard_SLView;1.CATEGORY_ID} = 26) OR
({spScorecard_SLView;1.CATEGORY_ID} = 41)) THEN
IF ({spScorecard_SLView;1.CM_TOTAL_CNT}> 0) AND
(({spScorecard_SLView;1.PM_3MM_TOTAL_CNT} = 0) OR
({spScorecard_SLView;1.3MM_TOTAL_CNT} = 0)) AND
({spScorecard_SLView;1.3MM_NC_CNT} > 0) AND
(((({spScorecard_SLView;1.3MM_TOTAL_CNT} - {spScorecard_SLView;1.3MM_NC_CNT})
/ {spScorecard_SLView;1.3MM_TOTAL_CNT}) * 100) >= 0.00) THEN 'Down Arrow' //
ELSE IF ((((({spScorecard_SLView;1.3MM_TOTAL_CNT} -
{spScorecard_SLView;1.3MM_NC_CNT}) / {spScorecard_SLView;1.3MM_TOTAL_CNT}) *
100) -((({spScorecard_SLView;1.PM_3MM_TOTAL_CNT} -
{spScorecard_SLView;1.PM_3MM_NC_CNT}) /
{spScorecard_SLView;1.PM_3MM_TOTAL_CNT}) * 100))/100) < 0.00 THEN
'Down Arrow'
I am stuck as to how to do something similar in PBI Report builder. Should I create a formula in the Value field under Value and States, and then delete any arrow settings under the Indicator States?
Can you create a formula using 'Down Arrow' etc in an IIf statement? I can only get indicator data returned when selecting 1 field under Value, but I need multiple fields & conditions.
SSRS Reports are similar to PBI Report builder so if there are any examples using it that may be of help. I am connecting to a SQL Server stored proc to pull back the data.
Thanks
Blowers

I would approach it like this...
Set a formula in the Indicator Value so that you return a number that corresponds to the arrow you want to show (e.g. return 1, 2 or 3)
You can use whatever format you feel comfotable with but I would suggest using the SWITCH() function rather than nested IIFs.
For example this checks two fields and returns one of three values, (this is just a random example to illustrate the point)
=SWITCH(
SUM(Fields!Amount.Value) >5000 AND Fields!Year.Value >2019, 1
, SUM(Fields!Amount.Value) >10000 AND Fields!Year.Value <2019, 2
, True, 3
)
Switch takes pairs of expressions and return values. It returns the value when it hits the first expression that evaluates to True. So this reads...
If the aggregated Amount is greater than 5000 and the Year >2019 then return 1
If the aggregated Amount is greater than 10000 and the Year <2019
then return 12
Else return 3
The final 'True', as it will always return true acts like an ELSE
Anyway, this will return a value of either 1, 2 or 3
Then in the Indicator Properties, just set the range for each indicator to 1, 2 or 3 like this

I ended up using IIF Logic to solve this, some of the calculations were too awkward in the end and it was easier for me to use IIF.
Using the 1,2,3 indicator values works well.
Here is the expression that i ended up using:
=IIF(Fields!CATEGORY_ID.Value = 4 AND Sum(Fields!PM_3MM_NC_CNT.Value -
Fields!Q3MM_NC_CNT.Value) < 0,1,
IIF(Fields!CATEGORY_ID.Value = 25 AND Sum(Fields!PM_3MM_NC_CNT.Value -
Fields!Q3MM_NC_CNT.Value) < 0,1,
IIF(Fields!CATEGORY_ID.Value = 4 AND Fields!PM_3MM_NC_CNT.Value <> 0
AND Fields!Q3MM_NC_CNT.Value <> 0 AND Fields!PM_3MM_NC_CNT.Value =
Fields!Q3MM_NC_CNT.Value ,2,
IIF(Fields!CATEGORY_ID.Value = 25 AND Fields!PM_3MM_NC_CNT.Value <> 0
AND Fields!Q3MM_NC_CNT.Value <> 0 AND Fields!PM_3MM_NC_CNT.Value =
Fields!Q3MM_NC_CNT.Value ,2,
IIF(Fields!CATEGORY_ID.Value = 4 AND Sum(Fields!PM_3MM_NC_CNT.Value -
Fields!Q3MM_NC_CNT.Value) > 0, 3,
IIF(Fields!CATEGORY_ID.Value = 25 AND Sum(Fields!PM_3MM_NC_CNT.Value -
Fields!Q3MM_NC_CNT.Value) > 0, 3,
IIF(Fields!CATEGORY_ID.Value = 4 AND Fields!PM_3MM_NC_CNT.Value = 0 AND
Fields!Q3MM_NC_CNT.Value = 0,4,
IIF(Fields!CATEGORY_ID.Value = 25 AND Fields!PM_3MM_NC_CNT.Value = 0
AND Fields!Q3MM_NC_CNT.Value = 0,4,
IIF(Fields!CATEGORY_ID.Value = 21 AND Fields!CM_TOTAL_CNT.Value > 0 AND
Fields!PM_3MM_TOTAL_CNT.Value =0,1,
IIF(Fields!CATEGORY_ID.Value = 26 AND Fields!CM_TOTAL_CNT.Value > 0 AND
Fields!PM_3MM_TOTAL_CNT.Value =0,1,
IIF(Fields!CATEGORY_ID.Value = 41 AND Fields!CM_TOTAL_CNT.Value > 0 AND
Fields!PM_3MM_TOTAL_CNT.Value =0,1,
IIF(Fields!CATEGORY_ID.Value = 21 AND Fields!Q3MM_TOTAL_CNT.Value = 0
AND Fields!Q3MM_NC_CNT.Value > 0 AND Fields!TrendCalculation1.Value -
Fields!TrendCalculation2.Value > 0.00,1,
IIF(Fields!CATEGORY_ID.Value = 26 AND Fields!Q3MM_TOTAL_CNT.Value = 0
AND Fields!Q3MM_NC_CNT.Value > 0 AND Fields!TrendCalculation1.Value -
Fields!TrendCalculation2.Value > 0.00,1,
IIF(Fields!CATEGORY_ID.Value = 41 AND Fields!Q3MM_TOTAL_CNT.Value = 0
AND Fields!Q3MM_NC_CNT.Value > 0 AND Fields!TrendCalculation1.Value -
Fields!TrendCalculation2.Value > 0.00,1,
IIF(Fields!CATEGORY_ID.Value <> 12 AND Fields!CATEGORY_ID.Value <> 30
AND Fields!PM_3MM_TOTAL_CNT.Value = 0,3,
IIF(Fields!CATEGORY_ID.Value <> 12 AND Fields!CATEGORY_ID.Value <> 30
AND Fields!Q3MM_TOTAL_CNT.Value = 0,3,
IIF(Fields!CATEGORY_ID.Value <> 12 AND Fields!CATEGORY_ID.Value <> 30
AND Fields!TrendCalculation1.Value - Fields!TrendCalculation2.Value <
0.00,1,
IIF(Fields!CATEGORY_ID.Value <> 12 AND Fields!CATEGORY_ID.Value <> 30
AND Fields!TrendCalculation1.Value - Fields!TrendCalculation2.Value >
0.00,3,
IIF(Fields!CATEGORY_ID.Value <> 12 AND Fields!CATEGORY_ID.Value <> 30
AND Fields!TrendCalculation1.Value - Fields!TrendCalculation2.Value =
0.00,2,
5)))))))))))))))))))

Related

T - SQL variable programming like in R/C++

I have sql data of 2,907,735 rows and 32 columns.. and I want to perform simple mathematical calculations for each row using the attributes in that row.
Currently, I had to program this in R which is taking so much time, and I'm not sure SQL has the capability to do this simple programming calculations are not.
If it has the capability, I think SQL will be faster.
Please help.
q1=0
q2=0
q3=0
q4=0
frac2final=0
sumcnt = 0
cumcnt = 0
year = 0
startpoint1 = 0
startpoint2 = 0
sumexpect = 0
cntatmind = 0
cntatmaxd = 0
exatmind = 0
exatmaxd = 0
ex1=0
ex2=0
ex3=0
ex4=0
cumcntfirst = 0
for (i in 1:2907735)
{
frac2final = a[i,25]
sumcnt = a[i,31]
cumcnt = frac2final + sumcnt
year = a[i,12]
sumexpect = a[i,32]
cntatmind = a[i,4]
cntatmaxd = a[i,5]
exatmind = a[i,6]
exatmaxd = a[i,7]
if(year !=2016) {
startpoint1 = 0
startpoint2=0}
else
{startpoint1 = frac2final
startpoint2 = frac2final}
#cumcntfirst = startpoint2 + cntatmind
if (startpoint1<=0.25) q1 = max( (min(0.25,cumcnt) - startpoint1),0) else q1=0
startpoint1 = startpoint1 + q1
if(startpoint1<=0.5) q2 = max( (min(0.5,cumcnt) - startpoint1),0) else q2=0
startpoint1 = startpoint1 + q2
if(startpoint1<=0.75) q3 = max( (min(0.75,cumcnt) - startpoint1),0) else q3=0
startpoint1 = startpoint1 + q3
q4 = max(0,sumcnt-q1-q2-q3)
a[i,33] = q1
a[i,34] = q2
a[i,35] = q3
a[i,36] = q4
}
Remember in SQL we work with sets, not loops. An "Intermediate result" would be an in intermediate result set, which is synonymous with a subquery (for the most part).
As an example using your R as a starting point:
SELECT q1,
startpoint1 + q1 AS startpoint1
FROM (
startpoint1,
SELECT CASE
WHEN startpoint1 <= 0.25
THEN CASE
WHEN 0 > CASE
WHEN 0.25 > cumcnt
THEN 0.25
ELSE cumcnt
END - startpoint1
THEN 0
ELSE CASE
WHEN 0.25 > cumcnt
THEN 0.25
ELSE cumcnt
END - startpoint1
END
ELSE 0
END AS q1
FROM (
SELECT CASE
WHEN year <> 2016
THEN 0
ELSE frac2final
END AS startpoint1,
CASE
WHEN year <> 2016
THEN 0
ELSE frac2final
END AS startpoint2
FROM yourTable
) subquery1
) subquery2
There is no function to replicate your min and max here so we have to use CASE statements to do the logic. There are many examples of UDFs out there to give this functionality though. The trick is that min and max DO exist in tsql, but it takes the min/max of values in a column when aggregating many records together. Your use here is synonymous with UDFs generally called GREATEST() and LEAST() to compare two or more fields from the same record.
To get a grasp on how this is working, run the inner-most subquery and see what it produces, then run the next subquery (which has subquery1 inside of it) to see how it uses those results... then you can just reuse this logic to build your remaining fields for your final result set.

RunningSum inside another

I have a report with the following data
IdSource 20170629 20170628 20170626 20170625 20170624 20170623
Id1. OK KO N/A KO OK KO
I want to count the number of days my data (status of a workflow) is KO. N/A means the worflow is not expected, so it should not be counted.
Expected results :
IdSource 20170629 20170628 20170626 20170625 20170624 20170623
Id1. OK KO(2) N/A KO(1) OK KO(1)
The count must be reset at each OK.
In SQL, I would do
select t.*,
sum(case when status = 'KO' then 1 else 0 end) over (partition by id, cume_ko order by date) as nbDayKO
from (select t.*,
sum(case when status = 'OK' then 1 else 0 end) over (partition by id order by date) as cume_ko
from t
) t
I tried to use the RunningSum function without success:
I need to inverse the sort on the date
I cannot use the result of a runningSum inside another.
Thanks for your help
It's ugly and not very efficient, but I did not find any other solutions. Here is how I did it :
If ([status] = 0) Then 0
Else (
(If (RelativeValue([status];([dayOfWeek]);1) = 1) Then
If (RelativeValue([status];([dayOfWeek]);2) = 1) Then
If (RelativeValue([status];([dayOfWeek]);3) = 1) Then
If (RelativeValue([status];([dayOfWeek]);4) = 1) Then
If (RelativeValue([status];([dayOfWeek]);5) = 1) Then
If (RelativeValue([status];([dayOfWeek]);6) = 1) Then
If (RelativeValue([status];([dayOfWeek]);7) = 1) Then
If (RelativeValue([status];([dayOfWeek]);8) = 1) Then
If (RelativeValue([status];([dayOfWeek]);9) = 1) Then
If (RelativeValue([status];([dayOfWeek]);10) = 1) Then
If (RelativeValue([status];([dayOfWeek]);11) = 1) Then
If (RelativeValue([status];([dayOfWeek]);12) = 1) Then
If (RelativeValue([status];([dayOfWeek]);13) = 1) Then
If (RelativeValue([status];([dayOfWeek]);14) = 1) Then
If (RelativeValue([status];([dayOfWeek]);15) = 1) Then 15
Else 14
Else 13
Else 12
Else 11
Else 10
Else 9
Else 8
Else 7
Else 6
Else 5
Else 4
Else 3
Else 2
Else 1
Else 0)+[status])

visual basics equal to or greater than

I am trying to write some code that says if textbox1 is equal to 0 between 10 then HandDecimal = 1. Else if textbox1 is equal to 10.1 through 100, then HandDecimal = 3. Else if textbox1 is equal to 100.1 and greater than HandDecimal = 5.
Here is my code, but it does not seem to work for me.
If WeightDecimal = 0 <= 10 Then
HandDecimal = 1
ElseIf WeightTextBox.Text = 10 <= 100 Then
HandDecimal = 3
ElseIf WeightTextBox.Text >= 100.1 Then
HandDecimal = 5
End If
How do I have to change the code to make it work?
Dim weight as Decimal = Decimal.Parse(WeightTextBox.Text)
If weight >= 0 AndAlso weight <= 10 Then
HandDecimal = 1
ElseIf weight > 10 AndAlso weight <= 100 Then
HandDecimal = 3
ElseIf weight > 100 Then
HandDecimal = 5
End If
Select Case statement with To operator
Select Case WeightDecimal
Case 0 To 10
HandDecimal = 1
Case 10.1 To 100
HandDecimal = 3
Case Else
HandDecimal = 5
End Select

Understanding case expression in the "Where" clause

I've got this code here and you can see from my Pseudocode what I'm trying to accomplish
select *
from dbo.BenefitsForms
inner join Dependents on BenefitsForms.UserId = Dependents.BenefitsForm_UserId
inner join CoverageLevels on BenefitsForms.MedicalId = CoverageLevels.Id
where (BenefitsForms.MedicalId > 0 AND BenefitsForms.MedicalId < 13)
AND Dependents.IsSpouse = CASE when CoverageLevels.[Level] = 2 then 1
when CoverageLevels.[Level] = 3 then 0 end
when CoverageLevels.[Level] = 4 then [any, it doesnt matter] <--- my desire but it doesn't work.
What can I do to get the effect I desire in the brackets? If Coverage Level = 4 then I don't care what Dependents.IsSpouse is, I don't even need to sort by it anymore.
Assuming that isSpouse can only be 0 or 1... if CoverageLevels.Level is 4, then compare isSpouse to itself, which will always result in true:
AND Dependents.IsSpouse = CASE
when CoverageLevels.[Level] = 2 then 1
when CoverageLevels.[Level] = 3 then 0
when CoverageLevels.[Level] = 4 then Dependents.IsSpouse
END
Alternately, this can also be expressed without the CASE:
WHERE
BenefitsForms.MedicalId > 0
AND BenefitsForms.MedicalId < 13
AND (
(Dependents.IsSpouse = 1 AND CoverageLevels.[Level] = 2)
OR (Dependents.IsSpouse = 0 AND CoverageLevels.[Level] = 3)
OR CoverageLevels.[Level] = 4
)

SQL Counting Records with Count and Having

I'm having problems with what I thought was a simple query to count records:
SELECT req_ownerid, count(req_status_lender) AS total6
FROM bor_requests
WHERE (req_status_lender = 0 AND req_status_borrower = 0) OR
(req_status_lender = 1 AND req_status_borrower = 1)
GROUP BY req_ownerid
HAVING req_ownerid = 70
I thought this would count all the records where (req_status_lender = 0 AND req_status_borrower = 0) and (req_status_lender = 1 AND req_status_borrower = 1) and then give me the total but it only gives me the total for either (req_status_lender = 0 AND req_status_borrower = 0) or (req_status_lender = 1 AND req_status_borrower = 1).
Any ideas what I'm doing wrong?
You should use the HAVING clause only to limit on something that's been aggregated in your query above - e.g. if you want to select all those rows where a SUM(....) or COUNT(...) is larger than say 5, then you'd use HAVING SUM(...) > 5
What you're doing here is a standard WHERE clause - add it there!
SELECT req_ownerid, count(req_status_lender) AS total6
FROM bor_requests
WHERE req_ownerid = 70
AND ((req_status_lender = 0 AND req_status_borrower = 0) OR
(req_status_lender = 1 AND req_status_borrower = 1))
GROUP BY req_ownerid