I have a commission banding table containing the following:
Lower Upper Percentage
0 300 45
300.01 800 50
800.01 1500 55
The lower and upper amounts are currency values, and i need to calculate a cumulative amount to pay based on the total sales, using the percentage amount relevant to the total sales.
If i then have a total sales amount of 350, my commission should be calculated as the following:
first 300 of the total would be using the 45%
the remaining 50 would be using the 50%
so my total would be
300*45% = 135
50*50% = 25
Total = 160
I am updating a table with the amounts via a sproc so would need to accommodate this in there.
How is the best way to go about this?
Note : the sproc below has the correct column names, where as the example above i changed the names of the columns for simplicity. The SPROC also joins the table where the bands are stored, and the update table is a kind of working/reporting table
EDIT : sproc update section:
UPDATE CommissionCalculationDetails
SET TotalCommissionAmount =
case
when TotalSales > Upper then Upper
when TotalSale > Lower then #sales - Lower
else 0
end
* Percentage / 100
FROM CommissionCalculationDetails
LEFT JOIN CommissionBand
ON TotalSales > CommissionBand.Lower
AND TotalSales < CommisionBand.Upper
I propose that you store non-inclusive lower bounds instead (ex: 300 instead of 300.01) and use strictly greater than when comparing against it. As it stands, the value 300.005 would not be correctly categorized.
If this was the case, you could compute the total commission using the following query:
select
sum (
case
when #sales > Upper then Upper
when #sales > Lower then #sales - Lower
else 0
end
* Percentage / 100
) as TotalCommission
from CommissionTable
Here's an online test version of this: http://www.sqlfiddle.com/#!3/87f12/8
Slightly offtopic: Your table currently contains redundant information; each lower bound is (more or less) equal to the previous upper bound. Although this is not essential, you could think of a way to store, for example, upper bounds only (and have a null signifying unbounded).
For the update, one possible solution is to extract the commission calculation in a function, like so:
create function ufn_CalculateCommission(#Sales money)
returns money
as
begin
declare #result money
select
#result =
sum (
case
when #sales > Upper then Upper
when #sales > Lower then #sales - Lower
else 0
end
* Percentage / 100
)
from CommissionBand
return #result
end
After declaring the function, the update can be simplified to:
update CommissionCalculationDetails
set TotalCommissionAmount = dbo.ufn_CalculateCommission(TotalSales);
Here's how it works: http://www.sqlfiddle.com/#!3/f4405/4
Related
I have a table with a column called "Points", the value of this column have a min value of 0, and a max value of 100.000. I have to do an analysis per range of this column, so I wrote a query like this:
select case when Points between 0 and 5000 then '0 - 5000'
when Points between 5001 and 20000 then '50001 - 20000'
when Points > 20000 then '> 20000'
else 0 end RangeP
from Sales
The problem is that the customer wants to see for each 2.000 points from 0 to 100.000
If I wrote the query using the case, the query will get really big, so I'd like one way to get dynamically this range.
It is possible? Thank you very much
You may create a table which contains the ranges, and their text labels, and then join to it, e.g.
SELECT
s.Points,
COALESCE(r.label, 'range not found') AS range
FROM Sales s
LEFT JOIN
(
SELECT 0 AS start, 2000 AS end, '0 - 2000' AS label UNION ALL
SELECT 2001, 4000, '2001 - 4000' UNION ALL
...
SELECT 98000, 100000, '98000 - 100000'
) r
ON s.Points BETWEEN r.start AND r.end;
I have inlined the table of ranges, but you may create a formal table (or maybe a temp table) instead, and then replace the above subquery with just a reference to that table.
I have a data set like that:
Data Set Contents
From To Comment
----+---+--------
0 50 Bad
50 70 Good
70 100 Excellent
If I have a value of 75, I need to get Excellent by searching the Dataset.
I know about the lookup function but it is not what I want. How can I do that?
The values should be in percentage.
Note : the value (75) is Average of a column (Calculated) it
calculate student grade from max and student mark Version SQL Server
2016
Note 2 : the dataset is from database not static values
Thank You
Assuming you only ever have a fixed number of 'grades' then this will work. However, I would strongly recommend doing this type of work on the server where possible.
Here we go...
I created two datasets
dsGradeRange with the following sql to recreate your example (more or less)
DECLARE #t TABLE (low int, high int, comment varchar(20))
INSERT INTO #t VALUES
(0,49,'Bad'),
(50,69,'Good'),
(70,100, 'Excellent')
SELECT * FROM #t
dsRandomNumbers This just creates 30 random numbers between 0 and 100
SELECT *
FROM (SELECT top 30 ABS(CHECKSUM(NEWID()) % 100) as myNumber FROM sys.objects) x
ORDER BY myNumber
I added a table to the report to show the grades (just for reference).
I then added a table to show the dsRandomNumbers
Finally I set the expression of the 2nd column to the following expression.
=SWITCH
(
Fields!myNumber.Value < LOOKUP("Bad", Fields!comment.Value, Fields!high.Value, "dsGradeRange"), "Bad",
Fields!myNumber.Value < LOOKUP("Good", Fields!comment.Value, Fields!high.Value, "dsGradeRange"), "Good",
True, "Excellent"
)
This gives the following results
As you can see we only need to compare to the high value of each case, the first match will return the correct comment.
Right click on your dataset and add a calculated field. Go to Field Properties > Fields > Add and add the following expression, which descripes your scenario:
=IIF(Fields!Number.Value < 50, "Bad", "Good")
I am working on a project where people can search on dimensions.
We have the following table structure:
Product table:
width
length
height
People can search this DB by giving for example the following dimensions:
300x200x250
What we want is that we sort this result based on the best match. We the exact dimensions are found in the DB you will have an 100% match. So we do not only want to find the results with 100% match but for example 310x200x250 or 300x220x260 is also fine, except the match percentage will be lower.
We want to allow all results with a match of 90% or more or a limit of say 50 results.
The basic query is of course simpel:
SELECT * FROM products WHERE length >= 300 AND height >= 200 AND height >= 100 AND(FORMULE TO CALCULATE THE MATCH WHERE HIGHER THEN 90% OR MAX OF 50 RESULTS.
Any on how can give me a push in the right direction how to solve the percentage part and the max limit part.
Kind regards,
Erwin
You can try something like this
SELECT *,
CEIL((300/ width * 0.333 +
200/ length * 0.333 +
250 / height * 0.333) * 100) pcnt
FROM products
WHERE width >= 300
AND length >= 200
AND height >= 250
HAVING pcnt > 90
ORDER BY pcnt DESC
LIMIT 50
Output:
| ID | WIDTH | LENGTH | HEIGHT | PCNT |
|----|-------|--------|--------|------|
| 1 | 300 | 200 | 250 | 100 |
| 2 | 310 | 200 | 250 | 99 |
| 3 | 300 | 220 | 260 | 96 |
Here is SQLFiddle demo
You can try the following code
SELECT * FROM products,
(ABS(width-300)/300)-1 as percwidth,
(ABS(length-200)/200)-1 as perclength,
(ABS(length-200)/200)-1 as percheigth
WHERE percwidth >=0.9
AND
perclength >=0.9
AND
percheigth >=0.9
ORDER BY percwidth,perclength,percheigth DESC
LIMIT 50
I suppose that when you cannot return 50 results with more than 90% you want the higher percentages. You should also decide which dimension is more important in your order by
This is the simple version:
I assume you are creating this query with passed variables?
Lets call these $length, $width, $height.
Assuming your columns are defined as INTs or other numbers, you can compare without quotes.
You want to use <> or 'BETWEEN' compares
"SELECT * FROM products
WHERE (length>($length - 10) AND length<($length+10)
AND (height>($height- 10) AND length<($height+10)
AND (width>($width- 10) AND width<($height+10)";
Where 10 is your range.
if $length=100
translates to "length>(100-10) " etc.
You can also use BETWEEN
(length BETWEEN $length - 10 AND $length + 10)
BETWEEN requires smaller value first, higher value second
You could also create percentages:
length BETWEEN $length-($length*.1) AND $length+($length*.1)
Then
ORDER BY (height=$height AND width=$width AND length=$length) DESC, height*width*length
This puts the products with exact dimensions at the top of the list, then the rest, with closer the product dimensions are to the desired, the better they will sort.
You need something like this to get this query to work:
SELECT *
FROM (
SELECT whatever,
whatever,
MATCH_METRIC(length, 300, width, 200, height, 100) AS match
FROM products
WHERE length >= 300
AND width >= 200
AND height >= 100
) AS a
WHERE match >= 0.9
ORDER BY match DESC
LIMIT 50
This assumes you have a match metric formula in which a perfect match yields a value of 1.0 and partial or near matches yield smaller values.
Of course, you need to work out your MATCH_METRIC() formula; you didn't explain how that works.
Let's assume that if you're matching one dimension, you want to use a fractional measure of how far apart the two values are.
1.0 - (ABS(A-B)/A)
If you put 300 and 300 into this formula, you'll get 1.0, a perfect match. If you put in 300 and 330, you'll get 0.9, meaning a 10% difference. Then, let's assume you want to use that formula on all three dimensions, and choose the dimension with the worst match -- the smallest match metric of the three dimensions. Then you'll get this for your MATCH_METRIC.
LEAST(1.0-(ABS(length-300)/length),
1.0-(ABS(width-200)/width),
1.0-(ABS(height-100)/height)
)
for a MATCH_METRIC() function. There are plenty of other ways to compute a metric. You'll have to figure out which one is best for your application.
Here's a definition of a stored procedure to compute that MATCH_METRIC().
DELIMITER $$
DROP FUNCTION IF EXISTS MATCH_METRIC$$
CREATE FUNCTION MATCH_METRIC(
len FLOAT, targetlen FLOAT,
wid FLOAT, targetwid FLOAT,
hgt FLOAT, targethgt FLOAT
) RETURNS FLOAT
NO SQL DETERMINISTIC
COMMENT 'computes match between dimension and target dimension'
BEGIN
RETURN LEAST(1.0-(ABS(len-targetlen)/len),
1.0-(ABS(wid-targetwid)/wid),
1.0-(ABS(hgt-targethgt)/hgt)
);
END$$
DELIMITER ;
I was wondering if there was a way to change truncate into round when doing a top x percent.
For Example:
Select Top 10 Percent
HospMastID
,ClientID
,ControlGroup = 1
Into
#RandomTable
From
#ClientsTable
Order By
NewID()
At present when I have a total of 1176 original records it returns 117 as the top 10 percent. Just curious what the setting would be to change this. Since it is really truncating the original numbers instead of rounding it.
Thanks,
Scott
If you want to ROUND the results, you can calculate your own value to use in TOP (granted, this means that you need to first count the rows of your table, but it's the only way that I can think of doing this, since there isn't a setting for this):
DECLARE #TopPercent INT, #Top INT
SET #TopPercent = 10 -- use the value you want here
SELECT #Top = ROUND(COUNT(*)*CAST(#TopPercent AS DECIMAL(4,1))/100,0)
FROM #ClientsTable
SELECT TOP(#Top)
HospMastID,
ClientID,
ControlGroup = 1
INTO #RandomTable
FROM #ClientsTable
ORDER BY NEWID()
I need to increase price by 15% in some rows of a table named my_products, based on the refresh_time of the product itself.
I don't know how to write this query, I was trying:
UPDATE my_products SET price = (price + 15%) WHERE refresh_time like "%2013%"
But this doesn't work.
UPDATE my_products SET price = (price * 1.15) WHERE refresh_time like "%2013%"
Just multiply the amount times 1.15. The 1 keeps the original value and the .15 adds the additional 15%.