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()
Related
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")
Hey is there any way to create query with simple formula ?
I have a table data with two columns value_one and value_two both are decimal values. I want to select this rows where difference between value_one and value_two is grater then 5. How can i do this?
Can i do something like this ?
SELECT * FROM data WHERE (MAX(value_one, value_two) - MIN(value_one, value_two)) > 5
Example values
value_one, value_two
1,6
9,3
2,3
3,2
so analogical difs are: 5, 6, 1, 1 so the selected row would be only first and second.
Consider an example where smaller number is subtracted with a bigger number:
2 - 5 = -3
So, the result is a difference of two numbers with a negation sign.
Now, consider the reverse scenario, when bigger number is subtracted with the smaller number:
5 - 2 = 3
Pretty simple right.
Basically, the difference of two number remains same, if you just ignore the sign. This is in other words called absolute value of a number.
Now, the question arises how to find the absolute value in MySQL?
Answer to this is the built-in method of MySQL i.e. abs() function which returns an absolute value of a number.
ABS(X):
Returns the absolute value of X.
mysql> SELECT ABS(2);
-> 2
mysql> SELECT ABS(-32);
-> 32
Therefore, without worrying about finding min and max number, we can directly focus on the difference of two numbers and then, retrieving the absolute value of the result. Finally, check if it is greater than 5.
So, the final query becomes:
SELECT *
FROM data
WHERE abs(value_one - value_two) > 5;
You can also do complex operations once the absolute value is calculated like adding or dividing with the third value. Check the code below:
SELECT *
FROM
data
WHERE
(abs(value_one - value_two) / value_three) + value_four > 5;
You can also add multiple conditions using logical operators like AND, OR, NOT to do so. Click here for logical operators.
SELECT *
FROM
data
WHERE
((abs(value_one - value_two) / value_three) + value_four > 5)
AND (value_five != 0);
Here is the link with various functions available in MySQL:
https://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html
No, you would just use a simple where clause:
select *
from data
where abs(value_one - value_two) > 5;
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 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
Let's say I have a list of values, like this:
id value
----------
A 53
B 23
C 12
D 72
E 21
F 16
..
I need the top 10 percent of this list - I tried:
SELECT id, value
FROM list
ORDER BY value DESC
LIMIT COUNT(*) / 10
But this doesn't work. The problem is that I don't know the amount of records before I do the query. Any idea's?
Best answer I found:
SELECT*
FROM (
SELECT list.*, #counter := #counter +1 AS counter
FROM (select #counter:=0) AS initvar, list
ORDER BY value DESC
) AS X
where counter <= (10/100 * #counter);
ORDER BY value DESC
Change the 10 to get a different percentage.
In case you are doing this for an out of order, or random situation - I've started using the following style:
SELECT id, value FROM list HAVING RAND() > 0.9
If you need it to be random but controllable you can use a seed (example with PHP):
SELECT id, value FROM list HAVING RAND($seed) > 0.9
Lastly - if this is a sort of thing that you need full control over you can actually add a column that holds a random value whenever a row is inserted, and then query using that
SELECT id, value FROM list HAVING `rand_column` BETWEEN 0.8 AND 0.9
Since this does not require sorting, or ORDER BY - it is O(n) rather than O(n lg n)
You can also try with that:
SET #amount =(SELECT COUNT(*) FROM page) /10;
PREPARE STMT FROM 'SELECT * FROM page LIMIT ?';
EXECUTE STMT USING #amount;
This is MySQL bug described in here: http://bugs.mysql.com/bug.php?id=19795
Hope it'll help.
I realize this is VERY old, but it still pops up as the top result when you google SQL limit by percent so I'll try to save you some time. This is pretty simple to do these days. The following would give the OP the results they need:
SELECT TOP 10 PERCENT
id,
value
FROM list
ORDER BY value DESC
To get a quick and dirty random 10 percent of your table, the following would suffice:
SELECT TOP 10 PERCENT
id,
value
FROM list
ORDER BY NEWID()
I have an alternative which hasn't been mentionned in the other answers: if you access from any language where you have full access to the MySQL API (i.e. not the MySQL CLI), you can launch the query, ask how many rows there will be and then break the loop if it is time.
E.g. in Python:
...
maxnum = cursor.execute(query)
for num, row in enumerate(query)
if num > .1 * maxnum: # Here I break the loop if I got 10% of the rows.
break
do_stuff...
This works only with mysql_store_result(), not with mysql_use_result(), as the latter requires that you always accept all needed rows.
OTOH, the traffic for my solution might be too high - all rows have to be transferred.