19

I am using mySQL and CodeIgniter. I have some floating point numbers in my database such as

  • 8.3456
  • 8.5555
  • 4.5556

I want to...

SELECT * FROM table WHERE value = $myvalue

but I can't use value = $myvalue in my SELECT query because $myvalue is not exactly equal to database values. I need to get the nearest value to $myvalue from database.

If $myvalue is 5 I want to select the value 4.5556.

How can I do this in mySQL?

Kanishka Panamaldeniya
  • 17,302
  • 31
  • 123
  • 193
  • if you have a few rows then the answers towards the top will be sufficient - however if you have millions of rows i would not recommend them as they will scan the whole table - instead see my answer at the bottom (trade-off being readability as the query is much longer - but much faster too) – GrahamTheDev Jun 09 '14 at 15:12

11 Answers11

24
select * 
from table 
order by abs(value - $myvalue)
limit 1
Bohemian
  • 412,405
  • 93
  • 575
  • 722
17

Assuming that you have a 10% tolerance (+/-) you could try something like:

select * from table 
where value >= ($myvalue * .9) and value <= ($myvalue * 1.1) 
order by abs(value - $myvalue) limit 1

Slightly updated stealing from others - this should return the nearest result in the assumed tolerance range. (Also, I just noticed the where was incorrect, apologies - now it should work).

p.marino
  • 6,244
  • 3
  • 25
  • 36
  • What if the nearest $myvalue is more than your arbitrary % away from any value? – Bohemian Sep 01 '11 at 10:55
  • 1
    The original requirement is not very clear. Of course if there is no "accepted range" you can say that 10000000000 is close enough to "1", assuming there is nothing better in your DB. In this case you can drop the "where" part. – p.marino Sep 01 '11 at 10:58
  • 1
    Note that this will fail if `$Myvalue` is 0. If you really want to do this, you need to add `OR (($myvalue >= (value * .9)) AND ($myvalue <= (value * 1.1)))` – Johan Sep 01 '11 at 11:26
  • This will not show nearest value if he put 6 for example -> however you were on the right track with the >= and < so a +1 from me (and a +1 for not using `ABS` on a whole table!)-> anyone looking who sees this please see my answer bottom that will give a fast, correct result. – GrahamTheDev Jun 09 '14 at 15:20
16
(
select   *
from     table
where    value >= $myvalue
order by value asc
limit 1
)
union
(
select   *
from     table
where    value < $myvalue
order by value desc
limit 1
)
order by abs(value - $myvalue)
limit 1

This may look counter-intuitive but the speed will be greater than the other queries shown so far.

This is due to the fact that a greater than and less than query is quicker.

Then doing an ABS on two values is nothing.

This will give you the quickest return in a single query I can think of.

Doing an ABS on a whole table will be slow as it will scan the whole table.

Loïc Faure-Lacroix
  • 13,220
  • 6
  • 67
  • 99
GrahamTheDev
  • 22,724
  • 2
  • 32
  • 64
  • 1
    Looks like the best answer so far – Oleg Jan 10 '22 at 10:24
  • 1
    This is great! For even better performance (~50%), replace `UNION` with `UNION ALL`. (I deleted my previous comment: union is the fastest possible way) – phil294 Apr 04 '22 at 15:19
3

Get the largest value similar to $val:

SELECT * FROM tab WHERE val <= $val ORDER BY val DESC LIMIT 1

Get the smallest value similar to $val:

SELECT * FROM tab WHERE val >= $val ORDER BY val LIMIT 1

Get the closest value similar to $val in either direction:

SELECT * FROM tab ORDER BY abs(val - $val) LIMIT 1
SIDU
  • 2,258
  • 1
  • 12
  • 23
3

Take the first value from the following:

select * from table order by abs(value - $myvalue);
Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82
2

In my case, I was using the browsers geolocations and trying to find a closest city/state based on the coordinates I had in a table.

table structure:

id    zipcode    city_state   lat    lon
1     12345      Example, GA  85.3   -83.2

Recommend testing this vigorously before using -- probably needs some tweaks, but I came up with this as a start

SELECT city_state, 
   zipcode, 
   ( Abs( lat - -33.867886 ) 
     + Abs( lon - -63.987) ) AS distance
FROM   zipcodes 
ORDER  BY distance 
LIMIT  1;  

For laravel users:

$city = Zipcodes::selectRaw
    ('city_state, zipcode,  ( ABS( lat - ? ) + ABS( lon - ?) ) AS distance', [$lat, $lon])
        ->orderBy('distance')
        ->first();

echo $city->city_state

Hope this helps someone someday.

espradley
  • 2,138
  • 2
  • 17
  • 15
1
SELECT * FROM table1 ORDER BY ABS(value - '$myvalue') LIMIT 1 
Johan
  • 74,508
  • 24
  • 191
  • 319
0
SELECT number, ABS( number - 2500 ) AS distance
FROM numbers
ORDER BY distance
LIMIT 6

Selecting closest values in MySQL

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
UserMat
  • 600
  • 4
  • 10
  • 27
0

Try this:

SELECT *,abs((columnname -Yourvalue)) as near
  FROM table
 WHERE order by near limit 0,1
Pang
  • 9,564
  • 146
  • 81
  • 122
Vishal P Gothi
  • 987
  • 5
  • 15
0

Unfortunately, I think your database will probably do a full table scan for solutions that involve abs, so they will be (very) slow once your table grows. A fast-running solution may be found in this earlier thread specific to PostgreSQL.

Luc
  • 5,339
  • 2
  • 48
  • 48
Andrew Lazarus
  • 18,205
  • 3
  • 35
  • 53
-1

Read this page http://dev.mysql.com/doc/refman/5.1/en/mathematical-functions.html#function_round

but your select would look like this

select value from table where ROUND(value) = $myvalue 
Rob
  • 1,235
  • 2
  • 19
  • 44
  • 3
    What if the nearest `$myvalue` is more than 1 away from any value? – Bohemian Sep 01 '11 at 10:54
  • @Bohemian - at some point your going to need a cut off point for matching the value – Rob Sep 01 '11 at 10:58
  • @Bohemian, no you don't that depends entirely on your application, and **if** you need a cutoff, it should always be a percentage of the original values. What if you're not dealing with `8.12`, but with `15,481,254,454,875,544,545.1` instead? – Johan Sep 01 '11 at 11:21
  • The question is clear: "Pick the *closest* value". It doesn't say anything about *how far away* the value is... could be 1000 away. Your answer is, quite simply, "wrong". – Bohemian Sep 01 '11 at 13:10