Recursive scalar function - sql

I'm attempting to rebuild some VBA functions in SQL server 2016 and I'm having difficult making a recursive function work properly.
My attempt below is returning NULL and when I step through the function in debug #returnValue never seems to hold a value even though it appears to recurse correctly.
Can someone please advise how to fix this.
Example expected results:
select [dbo].TickDiff(1.01, 1.05) -- Expect result 4
select [dbo].TickDiff(2.02, 16) -- Expect result 121
Functions:
CREATE FUNCTION [dbo].TickDiff (#odds1 float, #odds2 float)
RETURNS float
AS
BEGIN
DECLARE #returnValue as float
IF (#odds1 > #odds2)
BEGIN
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksDown(#odds1), #odds2) + 1
END
ELSE
IF (#odds1 < #odds2)
BEGIN
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksUp(#odds1), #odds2) + 1
END
RETURN #returnValue;
END
GO
CREATE FUNCTION [dbo].TicksUp(
#odds float
)
RETURNS float
AS
BEGIN
RETURN
case
when #odds < 1 then 1.02
when #odds >= 1 and #odds <= 1.99 then #odds + 0.01
when #odds >= 2 and #odds <= 2.98 then #odds + 0.02
when #odds >= 3 and #odds <= 3.95 then #odds + 0.05
when #odds >= 4 and #odds <= 5.9 then #odds + 0.1
when #odds >= 6 and #odds <= 9.8 then #odds + 0.2
when #odds >= 10 and #odds <= 19.5 then #odds + 0.5
when #odds >= 20 and #odds <= 29 then #odds + 1.0
when #odds >= 30 and #odds <= 48 then #odds + 2.0
when #odds >= 50 and #odds <= 95 then #odds + 5.0
when #odds >= 100 and #odds < 1000 then #odds + 10
when #odds >= 1000 then 1000
end
END
GO
CREATE FUNCTION [dbo].TicksDown(
#odds float
)
RETURNS float
AS
BEGIN
RETURN
case
when #odds <= 1.01 then 1.01
when #odds >= 1.01 and #odds <= 2 then #odds - 0.01
when #odds >= 2.02 and #odds <= 3 then #odds - 0.02
when #odds >= 3.05 and #odds <= 4 then #odds - 0.05
when #odds >= 4.1 and #odds <= 6 then #odds - 0.1
when #odds >= 6.2 and #odds <= 10 then #odds - 0.2
when #odds >= 10.5 and #odds <= 20 then #odds - 0.5
when #odds >= 21 and #odds <= 30 then #odds - 1.0
when #odds >= 32 and #odds <= 50 then #odds - 2.0
when #odds >= 55 and #odds <= 100 then #odds - 5.0
when #odds >= 110 and #odds < 1000 then #odds - 10
when #odds >= 1000 then 990
end
END
GO
The VBA functions I'm trying to copy are below:
Function TicksDown(ByVal odds As Currency) As Currency
Dim IncrementOdds As Currency
Select Case odds
Case 1.01 To 2
IncrementOdds = 0.01
Case 2.02 To 3
IncrementOdds = 0.02
Case 3.05 To 4
IncrementOdds = 0.05
Case 4.1 To 6
IncrementOdds = 0.1
Case 6.2 To 10
IncrementOdds = 0.2
Case 10.5 To 20
IncrementOdds = 0.5
Case 21 To 30
IncrementOdds = 1
Case 32 To 50
IncrementOdds = 2
Case 55 To 100
IncrementOdds = 5
Case 110 To 1000
IncrementOdds = 10
End Select
If Math.Round(odds - IncrementOdds, 2) >= 1.01 Then
TicksDown = Math.Round(odds - IncrementOdds, 2)
Else
TicksDown = 1.01
End If
End Function
Function TicksUp(ByVal odds As Currency) As Currency
Dim IncrementOdds As Currency
Select Case odds
Case 1 To 1.99
IncrementOdds = 0.01
Case 2 To 2.98
IncrementOdds = 0.02
Case 3 To 3.95
IncrementOdds = 0.05
Case 4 To 5.9
IncrementOdds = 0.1
Case 6 To 9.8
IncrementOdds = 0.2
Case 10 To 19.5
IncrementOdds = 0.5
Case 20 To 29
IncrementOdds = 1
Case 30 To 48
IncrementOdds = 2
Case 50 To 95
IncrementOdds = 5
Case 100 To 1000
IncrementOdds = 10
End Select
If Math.Round(odds + IncrementOdds, 2) <= 1000 Then
TicksUp = Math.Round(odds + IncrementOdds, 2)
Else
TicksUp = 1000
End If
End Function
Function TickDiff(odds1 As Currency, odds2 As Currency) As Long
If odds1 > odds2 Then
odds1 = TicksDown(odds1)
TickDiff = TickDiff(odds1, odds2) + 1
ElseIf odds1 < odds2 Then
odds1 = TicksUp(odds1)
TickDiff = TickDiff(odds1, odds2) + 1
Else
'Found, Exit Recursive Function
End If
End Function

You need to set your #ReturnValue to zero:
DECLARE #returnValue as float = 0.0
That being said, your second example exceeds SQLs max recursion of 32. You may want to rewrite without recursion. Here's an example: http://www.sql-server-helper.com/error-messages/msg-217.aspx

Your #returnValue is not initialized , so it is null. And
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksDown(#odds1), #odds2) + 1
evaluates to null.
Try
CREATE FUNCTION [dbo].TickDiff (#odds1 float, #odds2 float)
RETURNS float
AS
BEGIN
DECLARE #returnValue as float = 0.;
IF (#odds1 > #odds2)
BEGIN
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksDown(#odds1), #odds2) + 1
END
ELSE
IF (#odds1 < #odds2)
BEGIN
set #returnValue = #returnValue + [dbo].TickDiff([dbo].TicksUp(#odds1), #odds2) + 1
END
RETURN #returnValue;
END
Also case expressions can be simplified because sql-server evaluates when conditions in the order specified.
CREATE FUNCTION [dbo].TicksUp(
#odds float
)
RETURNS float
AS
BEGIN
RETURN
case
when #odds < 1 then 1.02
when #odds <= 1.99 then #odds + 0.01
when #odds <= 2.98 then #odds + 0.02
when #odds <= 3.95 then #odds + 0.05
when #odds <= 5.9 then #odds + 0.1
when #odds <= 9.8 then #odds + 0.2
when #odds <= 19.5 then #odds + 0.5
when #odds <= 29 then #odds + 1.0
when #odds <= 48 then #odds + 2.0
when #odds <= 95 then #odds + 5.0
when #odds < 1000 then #odds + 10
when #odds >= 1000 then 1000
end
END

Related

Updating constraint set rhs in docplex

I am trying to update the rhs of a constraint iteratively under a while loop in docplex, however, it does not work properly. As I analyze the output text file, while some constraints are updated as I want, others are not.
z_bar is a list consists of y+1 elements and is updated at each iteration. Constraint set I want to change the RHS of consists of (x+1)*(y+1) constraints. z_bar is related to the j indice, however since each constraint involves i and j indices, I have to update all. What do you think I am doing wrong?
Original constraint set:
for i in range(1, x + 1):
for j in range(1, y + 1):
sub_cbd.add_constraint(x_cbd[i, j] <= z_bar[j], ctname='constraint_name{0}{1}'.format(i, j))
Updating constraint set rhs attempt:
for i in range(1, x + 1):
for j in range(1, y + 1):
sub_cbd.get_constraint_by_name('constraint_name{0}{1}'.format(i, j)).rhs = z_bar[j]
For the updated z_bar: [0, 0, 0, 0, 1, 1, 0...0], 15 elements in total, two 1s and 13 0s.
How it looks now:
constraint11: x_1_1 <= 0
constraint12: x_1_2 <= 0
constraint13: x_1_3 <= 0
constraint14: x_1_4 <= 0
constraint15: x_1_5 <= 1
constraint16: x_1_6 <= 1
constraint17: x_1_7 <= 0
constraint18: x_1_8 <= 0
constraint19: x_1_9 <= 0
constraint110: x_1_10 <= 0
constraint111: x_1_11 <= 1
constraint112: x_1_12 <= 1
constraint113: x_1_13 <= 1
constraint114: x_1_14 <= 1
constraint115: x_1_15 <= 1
constraint21: x_2_1 <= 0
constraint22: x_2_2 <= 0
constraint23: x_2_3 <= 0
constraint24: x_2_4 <= 0
constraint25: x_2_5 <= 1
constraint26: x_2_6 <= 1
constraint27: x_2_7 <= 0
constraint28: x_2_8 <= 0
constraint29: x_2_9 <= 0
constraint210: x_2_10 <= 0
constraint211: x_2_11 <= 1
constraint212: x_2_12 <= 1
constraint213: x_2_13 <= 1
constraint214: x_2_14 <= 1
constraint215: x_2_15 <= 1
How it should look:
constraint11: x_1_1 <= 0
constraint12: x_1_2 <= 0
constraint13: x_1_3 <= 0
constraint14: x_1_4 <= 0
constraint15: x_1_5 <= 1
constraint16: x_1_6 <= 1
constraint17: x_1_7 <= 0
constraint18: x_1_8 <= 0
constraint19: x_1_9 <= 0
constraint110: x_1_10 <= 0
constraint111: x_1_11 <= 0
constraint112: x_1_12 <= 0
constraint113: x_1_13 <= 0
constraint114: x_1_14 <= 0
constraint115: x_1_15 <= 0
constraint21: x_2_1 <= 0
constraint22: x_2_2 <= 0
constraint23: x_2_3 <= 0
constraint24: x_2_4 <= 0
constraint25: x_2_5 <= 1
constraint26: x_2_6 <= 1
constraint27: x_2_7 <= 0
constraint28: x_2_8 <= 0
constraint29: x_2_9 <= 0
constraint210: x_2_10 <= 0
constraint211: x_2_11 <= 0
constraint212: x_2_12 <= 0
constraint213: x_2_13 <= 0
constraint214: x_2_14 <= 0
constraint215: x_2_15 <= 0
Full working example in https://github.com/AlexFleischerParis/zoodocplex/blob/master/zooincremental.py
from docplex.mp.model import Model
# original model
mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
mdl.add_constraint(nbbus40*40 + nbbus30*30 >= 300, 'kids')
mdl.minimize(nbbus40*500 + nbbus30*400)
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
#now 350 kids instead of 300
print()
print("now 350 kids instead of 300")
mdl.get_constraint_by_name("kids").rhs=350;
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
# no more than 4 buses 40 seats
print()
print("no more than 4 buses 40 seats")
mdl.get_var_by_name("nbBus40").ub=4
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
#change the objective so that cost for 40 seats is 450
#and remove the limit on the number of buses 40 seats
print()
print("change the objective so that cost for 40 seats is 450")
print("and remove the limit on the number of buses 40 seats ")
mdl.get_var_by_name("nbBus40").ub=1000
mdl.set_objective("min",nbbus40*450 + nbbus30*400);
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
Now let me change this into a loop:
from docplex.mp.model import Model
# original model
mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
for i in range(0,10):
mdl.add_constraint(nbbus40*40 + nbbus30*30*i >= 300+i,'kids'+str(i))
mdl.minimize(nbbus40*500 + nbbus30*400)
mdl.solve()
for v in mdl.iter_integer_vars():
print(v," = ",v.solution_value)
#now 350 kids instead of 300
for i in range(0,10):
mdl.get_constraint_by_name("kids"+str(i)).rhs=350+i;
mdl.export("zoo.lp")
mdl.solve()
and then in zoo.lp I get
Minimize
obj: 500 nbBus40 + 400 nbBus30
Subject To
kids0: 40 nbBus40 >= 350
kids1: 40 nbBus40 + 30 nbBus30 >= 351
kids2: 40 nbBus40 + 60 nbBus30 >= 352
kids3: 40 nbBus40 + 90 nbBus30 >= 353
kids4: 40 nbBus40 + 120 nbBus30 >= 354
kids5: 40 nbBus40 + 150 nbBus30 >= 355
kids6: 40 nbBus40 + 180 nbBus30 >= 356
kids7: 40 nbBus40 + 210 nbBus30 >= 357
kids8: 40 nbBus40 + 240 nbBus30 >= 358
kids9: 40 nbBus40 + 270 nbBus30 >= 359
Bounds
Generals
nbBus40 nbBus30
End

SQL - Update a table using LAG and based on another column

I have a dashboard with python and dash/plotly that receives inputs from the user and then run a query on Google BigQuery.
One of the queries updates a column (PAY_FT), uses other column (PAY_CLEAN) and a string input from the dashboard.
DECLARE cpi STRING DEFAULT "C";
UPDATE `xx.yy.zz` t
SET
PAY_FT = s.PAY_FT
FROM
(
SELECT
RN,
CASE
WHEN RN = 1 AND cpi = "S" AND PAY_SHALE_COR = "PAY" THEN 1
WHEN RN = 1 AND cpi = "C" AND PAY_CLEAN = "PAY" THEN 1
WHEN RN = 1 THEN 0
WHEN RN > 1 AND cpi = "S" AND PAY_SHALE_COR = "PAY" THEN 0.2 + (LAG(PAY_FT, 1) OVER(ORDER BY RN))
WHEN RN > 1 AND cpi = "C" AND PAY_CLEAN = "PAY" THEN 0.2 + (LAG(PAY_FT, 1) OVER(ORDER BY RN))
ELSE (LAG(PAY_FT, 1) OVER(ORDER BY RN))
END
AS PAY_FT
FROM
`xx.yy.zz`
WHERE
DEPTH_M IS NOT NULL
)
s
WHERE
t.RN = s.RN
But it's not working as intended. This query returns the following:
It works until DEPTH_M = 102.2, but after that it dows not return the 0.4 from the previous row. So, for example, if we had a "PAY" in DEPTH_M = 103.2, until 103.0 should return 0.4 and in 103.2 it should be 0.6.
How can I fix it?
Here's a sample of my data: SAMPLE.csv
My desired outpu should be:
DEPTH_M
PAY_CLEAN
PAY_FT
101.2
PAY
0.2
101.4
PAY
0.4
101.6
-
0.4
101.8
-
0.4
102.0
-
0.4
102.2
-
0.4
102.4
-
0.4
102.6
-
0.4
102.8
-
0.4
103.0
-
0.4
103.2
-
0.4
103.4
-
0.4
Let's say that we got another PAY on another row, the output should be:
DEPTH_M
PAY_CLEAN
PAY_FT
101.2
PAY
0.2
101.4
PAY
0.4
101.6
-
0.4
101.8
-
0.4
102.0
-
0.4
102.2
-
0.4
102.4
-
0.4
102.6
-
0.4
102.8
-
0.4
103.0
PAY
0.6
103.2
-
0.6
103.4
-
0.6
Thanks in advance.
on the second thought you can go with this query which is much simpler and does what you want and you don't need to check for RN :
SELECT RN
,0.2 * COUNT(CASE WHEN (cpi = "S" AND PAY_SHALE_COR = "PAY")
OR (cpi = "C" AND PAY_CLEAN = "PAY") THEN 1 END) OVER (ORDER BY RN)
+ SUM(CASE WHEN RN = 1 AND ((cpi = "S" AND PAY_SHALE_COR = "PAY")
OR (cpi = "C" AND PAY_CLEAN = "PAY")) THEN 0.8 ELSE 0 end) OVER () NEW_Pay_FT
FROM `xx.yy.zz`
WHERE DEPTH_M IS NOT NULL

How To Loop Nested If-Then Statements in VBA

I am going to try to simplify my objective as well as add all of my vba as my OP was not clear.
I am writing a macro that is to be used to determine a commissions percentage based on a particular strategies (Tier1, Tier2, BPO or Enterprise), a Gross Margin range and contract year. This will need to be looped through about 5,000 rows of data in the final product. I have been trying to nest multiple If-Then statements to achieve my goal however it is not working.
Below is the table for the commissions rates that apply to each of the strategies and then the code that I wrote for this nested If-Then statement.
Looking to try to make this simpler and loop it through the entirety of the rows with data. Goal is to have each cell in column J return a Commission rate determined by the strategy in column i, year in column D and GM in column Z. The strategy has the potential to vary each row down the page.
Would I be better off creating a custom Function?
Kinda crazy task for a first time macro writer. Appreciate all the feedback I have gotten already and look forward to any other ideas to come.
enter image description here
enter image description here
enter image description here
My Code:
Where Column I = Strategy
Where Column D = Year
Where Column Z = Gross Margin
Where Column J = Result of If-Then
where Column C is a defined data set which determines the number of rows in the workbook.
Sub Define_Comm_Rate
Dim LastRow As Long
LastRow = Range("C" & Rows.Count).End(xlUp).Row
If Sheet1.Range("I2") = "BPO" And Sheet1.Range("Z2") >= 0.24 Then
If Sheet1.Range("D2") = 1 Then
Sheet1.Range("J2") = 0.4
Else
If Sheet1.Range("D2") = 2 Then
Sheet1.Range("J2") = 0.3
Else: Sheet1.Range("J2") = 0.15
End If
End If
End If
If Sheet1.Range("I2") = "BPO" And Sheet1.Range("Z2") >= 0.21 And Sheet1.Range("Z2") < 0.24 Then
If Sheet1.Range("D2") = 1 Then
Sheet1.Range("J2") = 0.35
Else
If Sheet1.Range("D2") = 2 Then
Sheet1.Range("J2") = 0.25
Else: Sheet1.Range("J2") = 0.1
End If
End If
End If
If Sheet1.Range("I2") = "BPO" And Sheet1.Range("Z2") >= 0.18 And Sheet1.Range("Z2") < 0.21 Then
If Sheet1.Range("D2") = 1 Then
Sheet1.Range("J2") = 0.3
Else
If Sheet1.Range("D2") = 2 Then
Sheet1.Range("J2") = 0.2
Else: Sheet1.Range("J2") = 0.05
End If
End If
End If
If Sheet1.Range("I2") = "BPO" And Sheet1.Range("Z2") < 0.18 Then
If Sheet1.Range("D2") = 1 Then
Sheet1.Range("J2") = 0.25
Else
If Sheet1.Range("D2") = 2 Then
Sheet1.Range("J2") = 0.15
Else: Sheet1.Range("J2") = 0.05
End If
End If
End If
If Sheet1.Range("I2") = "Enterprise24" Then
If Sheet1.Range("D2") = "1" Then
Sheet1.Range("J2") = 0.4
Else
If Sheet1.Range("D2") = 2 Then
Sheet1.Range("J2") = 0.3
Else: Sheet1.Range("J2") = 0.15
End If
End If
End If
If Sheet1.Range("I2") = "Enterprise21" Then
If Sheet1.Range("D2") = 1 Then
Sheet1.Range("J2") = 0.35
Else
If Sheet1.Range("D2") = 2 Then
Sheet1.Range("J2") = 0.25
Else: Sheet1.Range("J2") = 0.1
End If
End If
End If
If Sheet1.Range("I2") = "Enterprise18" Then
If Sheet1.Range("D2") = 1 Then
Sheet1.Range("J2") = 0.3
Else
If Sheet1.Range("D2") = 2 Then
Sheet1.Range("J2") = 0.2
Else: Sheet1.Range("J2") = 0.05
End If
End If
End If
If Sheet1.Range("I2") = "Enterprise00" Then
If Sheet1.Range("D2") = 1 Then
Sheet1.Range("J2") = 0.25
Else
If Sheet1.Range("D2") = 2 Then
Sheet1.Range("J2") = 0.15
Else: Sheet1.Range("J2") = 0.05
End If
End If
End If
If Sheet1.Range("I2") = "Tier1" Then
If Sheet1.Range("Z2") > 0.4 Then
Sheet1.Range("J2") = 0.5
Else
If Sheet1.Range("Z2") <= 0.4 And Sheet1.Range("Z2") > 0.25 Then
Sheet1.Range("J2") = (1 * Sheet1.Range("Z2")) + 0.1
Else
If Sheet1.Range("Z2") <= 0.25 And Sheet1.Range("Z2") > 0.075 Then
Sheet1.Range("J2") = (2 * Sheet1.Range("Z2")) - 0.15
Else
If Sheet1.Range("Z2") <= 0.075 And Sheet1.Range("Z2") > 0 Then
Sheet1.Range("J2") = 0
Else: Sheet1.Range("J2") = 0.5
End If
End If
End If
End If
End If
If Sheet1.Range("I2") = "Tier1-100" Then
If Sheet1.Range("Z2") > 0.4 Then
Sheet1.Range("J2") = 0.5
Else
If Sheet1.Range("Z2") <= 0.4 And Sheet1.Range("Z2") > 0.25 Then
Sheet1.Range("J2") = (1 * Sheet1.Range("Z2")) + 0.1
Else
If Sheet1.Range("Z2") <= 0.25 And Sheet1.Range("Z2") > 0.075 Then
Sheet1.Range("J2") = (2 * Sheet1.Range("Z2")) - 0.15
Else
If Sheet1.Range("Z2") <= 0.075 And Sheet1.Range("Z2") > 0 Then
Sheet1.Range("J2") = 0
Else: Sheet1.Range("J2") = 0.5
End If
End If
End If
End If
End If
If Sheet1.Range("I2") = "Tier2" Then
If Sheet1.Range("Z2") > 0.35 Then
Sheet1.Range("J2") = 0.5
Else
If Sheet1.Range("Z2") <= 0.35 And Sheet1.Range("Z2") > 0.25 Then
Sheet1.Range("J2") = (1 * Sheet1.Range("Z2")) + 0.15
Else
If Sheet1.Range("Z2") <= 0.25 And Sheet1.Range("Z2") > 0.05 Then
Sheet1.Range("J2") = (2 * Sheet1.Range("Z2")) - 0.1
Else
If Sheet1.Range("Z2") <= 0.05 And Sheet1.Range("Z2") > 0 Then
Sheet1.Range("J2") = 0
Else: Sheet1.Range("J2") = 0.5
End If
End If
End If
End If
End If
If Sheet1.Range("I2") = "Tier2-100" Then
If Sheet1.Range("Z2") > 0.35 Then
Sheet1.Range("J2") = 0.5
Else
If Sheet1.Range("Z2") <= 0.35 And Sheet1.Range("Z2") > 0.25 Then
Sheet1.Range("J2") = (1 * Sheet1.Range("Z2")) + 0.15
Else
If Sheet1.Range("Z2") <= 0.25 And Sheet1.Range("Z2") > 0.05 Then
Sheet1.Range("J2") = (2 * Sheet1.Range("Z2")) - 0.1
Else
If Sheet1.Range("Z2") <= 0.05 And Sheet1.Range("Z2") > 0 Then
Sheet1.Range("J2") = 0
Else: Sheet1.Range("J2") = 0.5
End If
End If
End If
End If
End If
Sheet1.Range("J2").AutoFill Destination:=Sheet1.Range("J2:J" & LastRow)
Application.Calculate
End Sub
I'm going to offer a non-VBA approach to this using INDIRECT, INDEX, MATCH, and a few tables. My thought is that instead of coding lots of nested IF's, with hard-coded values, in VBA, you should be able to do this with lookup tables. (Disclaimer: this was also a fun intellectual exercise.)
First, create a table similar to the Commissions Table you already have and name it your specific strategy, e.g. "BPO", under Formulas > Name Manager. I created mine on a separate sheet named "Tables". Note that I used 1 in row 1 as your max (and unrealistic) gross margin. I also added 1, 2, and 3 in cells B1, C1, and D1 respectively. You'd need to create similar tables for your other strategies, and put them under the BPO table.
Then in column J on your data tab, enter this formula: =INDEX(INDIRECT(I2),MATCH(Z2,INDIRECT(I2&"["&Z$1&"]"),-1),MATCH(D2,Tables!$A$1:$D$1,1))
This INDEX formula has 3 main parts:
INDIRECT(I2) - this returns the array comprising the table you have named "BPO" - so you know you're looking at the table appropriate to that particular strategy.
MATCH(Z2,INDIRECT(I2&"["&Z$1&"]"),-1) - this looks up your gross margin in column Z against the table (BPO), looking in the column[Gross Margin]. The last argument of MATCH (match type) is -1, meaning that it finds the smallest value that is greater than or equal to your gross margin (note that in the table, Gross Margin is sorted in descending order). So for example, if your Gross Margin is 0.22, the MATCH will return 0.2399.
MATCH(D2,Tables!$A$1:$D$1,1) - This looks up the year, and tries to find the largest value that is less than or equal to it. So if the year is 1, 2, or 3, the MATCH will return 1, 2, or 3, respectively, but if the year is greater than 3, the MATCH returns 3.
Columns AB and AC in the second screenshot are just the results of 2. and 3. above, included to show that the correct commission value is being returned. Note that the "Year Column" is not Year 2 or Year 3, but the 2nd or 3rd column in the BPO table, i.e. Year 1 or Year 2 respectively.
Thanks for all the input/feedback.
Due to added complexity of additional sales plans needing to be incorporated as well as needing the flexibility to add/remove sales plans at any time, I ended up writing a custom Function.
Function Commissions(Strategy As String, GM As Variant, YR As Variant) As Variant
If Strategy = "BPO" And GM >= 0.24 And YR = 1 Then
Commissions = 0.4
ElseIf Strategy = "BPO" And GM >= 0.24 And YR = 2 Then
Commissions = 0.3
ElseIf Strategy = "BPO" And GM >= 0.24 And YR >= 3 Then
Commissions = 0.15
ElseIf Strategy = "BPO" And GM >= 0.21 And GM < 0.24 And YR = 1 Then
Commissions = 0.35
ElseIf Strategy = "BPO" And GM >= 0.21 And GM < 0.24 And YR = 2 Then
Commissions = 0.25
ElseIf Strategy = "BPO" And GM >= 0.21 And GM < 0.24 And YR >= 3 Then
Commissions = 0.1
ElseIf Strategy = "BPO" And GM >= 0.18 And GM < 0.21 And YR = 1 Then
Commissions = 0.3
ElseIf Strategy = "BPO" And GM >= 0.18 And GM < 0.21 And YR = 2 Then
Commissions = 0.2
ElseIf Strategy = "BPO" And GM >= 0.18 And GM < 0.21 And YR >= 3 Then
Commissions = 0.05
ElseIf Strategy = "BPO" And GM < 0.18 And YR = 1 Then
Commissions = 0.25
ElseIf Strategy = "BPO" And GM < 0.18 And YR = 2 Then
Commissions = 0.15
ElseIf Strategy = "BPO" And GM < 0.18 And YR >= 3 Then
Commissions = 0.05
''all other strategies continued below....''
End If
End Function

Rounding up decimal number with condition in T-SQL

I have a ASP function as below for rounding up the amount:
function GetRoundedVal(amount)
NoOfRight = right(formatnumber(amount,2),1)
if NoOfRight = 0 then
roundedAmount = amount
elseif NoOfRight = 1 then
roundedAmount = amount - 0.01
elseif NoOfRight = 2 then
roundedAmount = amount - 0.02
elseif NoOfRight = 3 then
roundedAmount = amount + 0.02
elseif NoOfRight = 4 then
roundedAmount = amount + 0.01
elseif NoOfRight = 5 then
roundedAmount = amount
elseif NoOfRight = 6 then
roundedAmount = amount - 0.01
elseif NoOfRight = 7 then
roundedAmount = amount - 0.02
elseif NoOfRight = 8 then
roundedAmount = amount + 0.02
elseif NoOfRight = 9 then
roundedAmount = amount + 0.01
else
end if
GetRoundedVal = roundedAmount
end function
The result should be like this:
+----------------+--------+
| Original Value | Result |
+----------------+--------+
| 19.91 | 19.90 | Original Value - 0.01
| 19.92 | 19.90 | Original Value - 0.02
| 19.93 | 19.95 | Original Value + 0.02
| 19.94 | 19.95 | Original Value + 0.01
| 19.95 | 19.95 |
| 19.96 | 19.95 | Original Value - 0.01
| 19.97 | 19.95 | Original Value - 0.02
| 19.98 | 20.00 | Original Value + 0.02
| 19.99 | 20.00 | Original Value + 0.01
+----------------+--------+
The question is can we do this directly on T-SQL?
SQL Server V14.
If it even possible.
Thanks.
You can try this.
DECLARE #MyTable TABLE(OriginalValue DECIMAL(18,2))
INSERT INTO #MyTable VALUES (19.91), (19.92), (19.93), (19.94), (19.95), (19.96), (19.97), (19.98), (19.99)
SELECT
OriginalValue,
ROUND( ( OriginalValue / 0.05 ), 0, 0 ) * 0.05 Result
FROM #MyTable
Result:
OriginalValue Result
--------------------------------------- ---------------------------------------
19.91 19.90000000
19.92 19.90000000
19.93 19.95000000
19.94 19.95000000
19.95 19.95000000
19.96 19.95000000
19.97 19.95000000
19.98 20.00000000
19.99 20.00000000
try This
DECLARE #Inp TABLE
(
Org DECIMAL(10,4),
Res DECIMAL(10,2)
)
INSERT INTO #Inp
(
Org
)
VALUES(19.91),
(19.92),
(19.93),
(19.94),
(19.95),
(19.96),
(19.97),
(19.98),
(19.99)
SELECT
*,
Result = CAST(
Org +
CASE RIGHT(CAST(Org AS DECIMAL(10,2)),1)
WHEN 1 THEN -0.01
WHEN 2 THEN -0.02
WHEN 3 THEN 0.02
WHEN 4 THEN 0.01
WHEN 5 THEN 0
WHEN 6 THEN -0.01
WHEN 7 THEN -0.02
WHEN 8 THEN 0.02
WHEN 9 THEN 0.01
ELSE 0 END
AS DECIMAL(10,2))
FROM #Inp
----------

End If without Block If error VBA

I wrote this code to try and assign a value to a variable based on the value of another variable generated using vba's Rnd() function and if statements but for some reason its giving me the "end if without block if error." This is just a portion of the code, I iterate this process 5 times for the 5 different products and do 10000 iterations of the number generators aggregating the results. Initially I tried it this way nesting everything, but when that didnt work I tried doing single if statements and same deal. Any help with this would be awesome.
For i = 0 To 10000
ProdE = Rnd()
ProdF = Rnd()
ProdG = Rnd()
ProdH = Rnd()
ProdI = Rnd()
If ProdE <= 0.1 Then DaysLateE = 2
If 0.1 < ProdE <= 0.2 Then DaysLateE = 3
If 0.2 < ProdE <= 0.3 Then DaysLateE = 4
If 0.3 < ProdE <= 0.4 Then DaysLateE = 5
If 0.4 < ProdE <= 0.5 Then DaysLateE = 6
If 0.5 < ProdE <= 0.6 Then DaysLateE = 7
If 0.6 < ProdE <= 0.7 Then DaysLateE = 8
If 0.7 < ProdE <= 0.8 Then DaysLateE = 9
If 0.8 < ProdE <= 0.9 Then DaysLateE = 10
If 0.9 < ProdE <= 1 Then DaysLateE = 11
End If
End If
End If
End If
End If
End If
End If
End If
End If
End If
TotalDaysLateE = DaysLateE + 8
SumDaysLateE = SumDaysLateE + TotalDaysLateE
If TotalDaysLateE > 15 Then CountE = CountE + 1
End If
The syntax for If allows two variants:
"Inline"
If {bool-expression} Then {do something}
"Block"
If {bool-expression} Then
{do something}
End If
An End If token is illegal when you're using the "inline" syntax.
So this (i.e. removing the End If tokens) makes your code compilable again:
If ProdE <= 0.1 Then DaysLateE = 2
If 0.1 < ProdE <= 0.2 Then DaysLateE = 3
If 0.2 < ProdE <= 0.3 Then DaysLateE = 4
If 0.3 < ProdE <= 0.4 Then DaysLateE = 5
If 0.4 < ProdE <= 0.5 Then DaysLateE = 6
If 0.5 < ProdE <= 0.6 Then DaysLateE = 7
If 0.6 < ProdE <= 0.7 Then DaysLateE = 8
If 0.7 < ProdE <= 0.8 Then DaysLateE = 9
If 0.8 < ProdE <= 0.9 Then DaysLateE = 10
If 0.9 < ProdE <= 1 Then DaysLateE = 11
However, as #Rohan K suggested, a better option would be to use a Select Case construct, because right now, all these conditions are evaluated all the time - with a Select Case block, execution would exit the Select block after finding a matching condition, and as a bonus you gain readabiilty:
Select Case ProdE
Case Is <= 0.1
DaysLateE = 2
Case Is <= 0.2
DaysLateE = 3
Case Is <= 0.3
DaysLateE = 4
Case Is <= 0.4
DaysLateE = 5
Case Is <= 0.5
DaysLateE = 6
Case Is <= 0.6
DaysLateE = 7
Case Is <= 0.7
DaysLateE = 8
Case Is <= 0.8
DaysLateE = 9
Case Is <= 0.9
DaysLateE = 10
Case Is <= 1
DaysLateE = 11
Case Else
'DaysLateE = ??
End Select
So, what happens when ProdE is greater than or equal to 1? (hadn't read where ProdE came from, nevermind) It seems there's a straight linear relationship between the value of ProdE and DaysLateE - you could try to come up with a formula to calculate it instead.
This probably isn't perfect, but comes pretty close:
DaysLateE = Int(ProdE * 10 - 0.000000000001) + 2
And then you don't need If or Select blocks.
The problem is your final If statement there. It is completely valid syntax to have an If statement all on one line without an End If. So when you put that End If there it is expecting an If statement with lines after it.
These two would be valid without an error
If TotalDaysLate > 15 then CountE = CountE + 1
Or
If TotalDaysLate > 15 Then
CountE = CountE + 1
End If
Try this: I would suggest Use Select Case incases like this
For i = 0 To 10000
ProdE = Rnd()
ProdF = Rnd()
ProdG = Rnd()
ProdH = Rnd()
ProdI = Rnd()
If ProdE <= 0.1 Then
DaysLateE = 2
End If
If 0.1 < ProdE And ProdE <= 0.2 Then
DaysLateE = 3
End If
If 0.2 < ProdE And ProdE <= 0.3 Then
DaysLateE = 4
End If
If 0.3 < ProdE And ProdE <= 0.4 Then
DaysLateE = 5
End If
If 0.4 < ProdE And ProdE <= 0.5 Then
DaysLateE = 6
End If
If 0.5 < ProdE And ProdE <= 0.6 Then
DaysLateE = 7
End If
If 0.6 < ProdE And ProdE <= 0.7 Then
DaysLateE = 8
End If
If 0.7 < ProdE And ProdE <= 0.8 Then
DaysLateE = 9
End If
If 0.8 < ProdE And ProdE <= 0.9 Then
DaysLateE = 10
End If
If 0.9 < ProdE And ProdE <= 1 Then
DaysLateE = 11
End If
TotalDaysLateE = DaysLateE + 8
SumDaysLateE = SumDaysLateE + TotalDaysLateE
If TotalDaysLateE > 15 Then
CountE = CountE + 1
End If
When you use single line ifs you don't need to write the End if. Check this: https://msdn.microsoft.com/en-us/library/office/gg251599.aspx
Thinking about your branching and conditional path, and for 10000 iterations, I'd suggest to just Bifurcate your If... Then statement. Better yet, use this in conjunction with two smaller Case... Select for an easily readable combination of all the suggestions. FASTER!
If ProdE <= 0.5 Then
If ProdE <= 0.1 Then DaysLateE = 2
If 0.1 < ProdE <= 0.2 Then DaysLateE = 3
If 0.2 < ProdE <= 0.3 Then DaysLateE = 4
If 0.3 < ProdE <= 0.4 Then DaysLateE = 5
If 0.4 < ProdE <= 0.5 Then DaysLateE = 6
Else
If 0.5 < ProdE <= 0.6 Then DaysLateE = 7
If 0.6 < ProdE <= 0.7 Then DaysLateE = 8
If 0.7 < ProdE <= 0.8 Then DaysLateE = 9
If 0.8 < ProdE <= 0.9 Then DaysLateE = 10
If 0.9 < ProdE <= 1 Then DaysLateE = 11
End If