• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

How to get "closest to" values in a query?

 
Sheriff
Posts: 67746
173
Mac Mac OS X IntelliJ IDE jQuery TypeScript Java iOS
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'll be writing a SQL select to fetch a record given latitude and longitude values.

The table contains discrete latitude and longitude values for various zip codes.

It is highly unlikely that the latitude and longitude value that 'll be using for input will exactly match values that are in the table.

What would be the most efficient/easy way to fetch the record that contains the latitude and longitude values that are closest to the input values?

P.S. I'm not sure if I'll be doing this in SQL or HQL yet, but I'm not sure it matters. I mention this in case it does!
 
ranger
Posts: 17347
11
Mac IntelliJ IDE Spring
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The thing is that you want longitute and latitudes that might be bigger or smaller than the number you are querying.

So it might need a few queries.

For instance if I knew I wanted numbers larger than lat and long, then I would have that in the where clause where lat > ? and long > ?

Then I would sort it by lat then long, like ORDER BY lat, long

I would also limit the number of records to 1 or 5, because the first record should be the closest to lat and long but where both are over the lat long given.

So this is where multiple queries come into play, using the combination of > and less than signs for lat and long, always sorting accordingly including asc and descending. In each query take the top record. Then at the end you might have to compare those final records and calculate which is actually closer.

That is my guess.

Mark

p.s. This is my first post from my Google TV.
 
Marshal
Posts: 28193
95
Eclipse IDE Firefox Browser MySQL Database
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You probably won't want to be doing trigonometry in your query, but given the data you're working with it would be okay to assume the earth is flat (or at least locally flat). And likewise, latitude degrees all have the same length everywhere but longitude degrees have a length which varies depending on the local latitude. (That's the trigonometry part.) But I think you can ignore that as in practice you aren't going to be comparing longitude degrees which are far apart in latitude.

Hopefully you have your data in decimal degrees, rather than in degrees, minutes, and seconds.

Then I think what you want to minimize is (ZipLatitude - InputLatitude) ^2 + (ZipLongitude - InputLongitude)^2. So you would write a query which ordered by that quantity and selected the first few rows of the result. Now, that number isn't the actual distance, it's just a surrogate for that. But once you have used it to identify the closest points from the database, you can then unleash the trigonometry to calculate the actual distance for the few points you actually choose.
 
Saloon Keeper
Posts: 27764
196
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This is a query I've addressed once or twice already.

While absolute distance on a sphere requires trig calculations, most of the interesting parts of the planet are in a zone where you can fake it reasonably well by using a "rectangular" frame. That is, between 2 limits of latitude and 2 limits of longitude, and that's a pair of simple "between" tests. You can use that as the coarse filter, and in many cases that's sufficient, or you can get by with doing the trig work on the filtered set using server logic.

For somewhat greater accuracy, you can assume a trapezoid instead of a rectangle, and that will complicate the query expression only slightly.

Of course, if you're locating polar bears, all bets are off.
 
Bear Bibeault
Sheriff
Posts: 67746
173
Mac Mac OS X IntelliJ IDE jQuery TypeScript Java iOS
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks guys, that was helpful. I've got it down to the following SQL query:

select ZipCode,sqrt( square(:latitudeValue - Latitude) + square(:longitudeValue - Longitude)) as distance from LOCATIONS order by distance

which, limited to a single result, gives me the record I need.

Now, I need to do this in HQL.
 
Paul Clapham
Marshal
Posts: 28193
95
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Most of the time, it gives you the record you need. When your input location is close to halfway between the two nearest ZIP locations, the approximation may cause you to choose a ZIP location which isn't quite the closest.

This might well not matter -- the centre of a ZIP code is a rather arbitrary choice anyway -- but if it does then you should select the first few records and recalculate the exact distance for each of them in your Java code. You should be able to track down that formula on the web.
 
Mark Spritzler
ranger
Posts: 17347
11
Mac IntelliJ IDE Spring
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Bear Bibeault wrote:Thanks guys, that was helpful. I've got it down to the following SQL query:

select ZipCode,sqrt( square(:latitudeValue - Latitude) + square(:longitudeValue - Longitude)) as distance from LOCATIONS order by distance

which, limited to a single result, gives me the record I need.

Now, I need to do this in HQL.



HQL based on a Java object called Location and another called CloseZip




Notice CloseZip is not mapped, it doesn't need to be

HQL

SELECT new CloseZip(location.zipCode, sqrt( square(:latitudeValue - location.latitude) + square(:longitudeValue - location.longitude)) AS distance)
FROM Location location
ORDER BY distance

I think you will have to concatenate the :latitudeValue and :longitudeValue in the Select part, I haven't tried putting bind named parameters in a Select portion. Maybe Hibernate will fill in those values.

Mark
 
Bear Bibeault
Sheriff
Posts: 67746
173
Mac Mac OS X IntelliJ IDE jQuery TypeScript Java iOS
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Mark. I'll give that a go!
reply
    Bookmark Topic Watch Topic
  • New Topic