Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find closest numeric value in database

Tags:

sql

I need to find a select statement that will return either a record that matches my input exactly, or the closest match if an exact match is not found.

Here is my select statement so far.

SELECT * FROM [myTable] 
WHERE Name = 'Test' AND Size = 2 AND PType = 'p' 
ORDER BY Area DESC

What I need to do is find the closest match to the 'Area' field, so if my input is 1.125 and the database contains 2, 1.5, 1 and .5 the query will return the record containing 1.

My SQL skills are very limited so any help would be appreciated.

like image 452
Tester101 Avatar asked Feb 26 '09 20:02

Tester101


People also ask

How do I find the greatest number in SQL?

SQL MIN() and MAX() Functions The MIN() function returns the smallest value of the selected column. The MAX() function returns the largest value of the selected column.

How do you use absolute function in SQL?

ABS() function : This function in SQL Server is used to return the absolute value of a specified number. Absolute value is used for depicting the distance of a number on the number line from 0. The direction of the number from zero is not considered since the absolute value of a number is never negative.


3 Answers

get the difference between the area and your input, take absolute value so always positive, then order ascending and take the first one

SELECT TOP 1 * FROM [myTable]  WHERE Name = 'Test' and Size = 2 and PType = 'p' ORDER BY ABS( Area - @input )  
like image 122
MikeW Avatar answered Sep 28 '22 05:09

MikeW


something horrible, along the lines of:

ORDER BY ABS( Area - 1.125 ) ASC LIMIT 1

Maybe?

like image 28
Ryan Graham Avatar answered Sep 28 '22 04:09

Ryan Graham


If you have many rows that satisfy the equality predicates on Name, Size, and PType columns then you may want to include range predicates on the Area column in your query. If the Area column is indexed this could allow efficient index-based access.

The following query (written using Oracle syntax) uses one branch of a UNION ALL to find the record with minimal Area >= your target, while the other branch finds the record with maximal Area < your target. One of these two records will be the record that you are looking for. Then you can ORDER BY ABS(Area - ?input) to pick the winner out of those two candidates. Unfortunately the query is complex due to nested SELECTS that are needed to enforce the desired ROWNUM / ORDER BY precedence.

SELECT *
FROM
  (SELECT * FROM
    (SELECT * FROM
       (SELECT * FROM [myTable]
         WHERE Name = 'Test' AND Size = 2 AND PType = 'p' AND Area >= ?target
         ORDER BY Area)
       WHERE ROWNUM < 2
     UNION ALL
     SELECT * FROM 
       (SELECT * FROM [myTable]
         WHERE Name = 'Test' AND Size = 2 AND PType = 'p' AND Area < ?target
         ORDER BY Area DESC)
       WHERE ROWNUM < 2)
     ORDER BY ABS(Area - ?target))
WHERE rownum < 2

A good index for this query would be (Name, Size, PType, Area), in which case the expected query execution plan would be based on two index range scans that each returned a single row.

like image 35
George Eadon Avatar answered Sep 28 '22 06:09

George Eadon