find the nearest location by latitude and longitude in postgresql - sql

Hi i'm trying find the nearest location by latitude and longitude in postgresql database.But when i run the below query it showing column distance does not exists.
ERROR: column "distance" does not exist
LINE 1: ... ) ) ) AS distance FROM station_location HAVING distance <...
^
********** Error **********
ERROR: column "distance" does not exist
SQL state: 42703
Character: 218
CREATE TABLE station_location
(
id bigint NOT NULL DEFAULT nextval('location_id_seq'::regclass),
state_name character varying NOT NULL,
country_name character varying NOT NULL,
locality character varying NOT NULL,
created_date timestamp without time zone NOT NULL,
is_delete boolean NOT NULL DEFAULT false,
lat double precision,
lng double precision,
CONSTRAINT location_pkey PRIMARY KEY (id)
)
SELECT *,( 3959 * acos( cos( radians(6.414478) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(12.466646) ) + sin( radians(6.414478) ) * sin( radians( lat ) ) ) ) AS distance
FROM station_location
HAVING distance < 5
ORDER BY distance
LIMIT 20;

PostGIS
Don't store lat and long on a table like that. Instead use an PostGIS geometry or geography type.
CREATE EXTENSION postgis;
CREATE TABLE foo (
geog geography;
);
CREATE INDEX ON foo USING gist(geog);
INSERT INTO foo (geog)
VALUES (ST_MakePoint(x,y));
Now when you need to query it, you can use KNN (<->) which will actually do this on an index.
SELECT *
FROM foo
ORDER BY foo.geog <-> ST_MakePoint(x,y)::geography;
In your query, you explicitly have HAVING distance < 5. You can do that on the index too.
SELECT *
FROM foo
WHERE ST_DWithin(foo.geog, ST_MakePoint(x,y)::geography, distance_in_meters)
ORDER BY foo.geog <-> ST_MakePoint(x,y)::geography;
This ensure that nothing is returned if all points lie outside of distance_in_meters.
Furthermore x and y are decimal numbers ST_MakePoint(46.06, 14.505)

select * from (
SELECT *,( 3959 * acos( cos( radians(6.414478) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(12.466646) ) + sin( radians(6.414478) ) * sin( radians( lat ) ) ) ) AS distance
FROM station_location
) al
where distance < 5
ORDER BY distance
LIMIT 20;

You can use PostgreSQL's cube and earthdistance extensions.
Enable them like this:
CREATE EXTENSION cube;
CREATE EXTENSION earthdistance;
Let's say your current location is 35.697933, 139.707318. Then your query will be something like this:
SELECT *, point(35.697933, 139.707318) <#> (point(longitude, latitude)::point) as distance
FROM station_location
-- WHERE (point(35.697933, 139.707318) <#> point(longitude, latitude)) < 3
ORDER BY distance;
Please note that the distance is in miles (by default).

See this gist, you will find how to declare a DOMAIN on the point type and how to override the distance operator to return the orthodromic distance.
Declare a latlong type inherited from point:
CREATE DOMAIN latlong AS point CHECK (VALUE[0] BETWEEN -90.0 AND 90.0 AND VALUE[1] BETWEEN -180 AND 180);
The orthodromic distance in kilometers (distance on a sphere with the earth radius):
CREATE OR REPLACE FUNCTION orthodromic_distance(latlong, latlong) RETURNS float AS $_$
SELECT acos(
sin(radians($1[0]))
*
sin(radians($2[0]))
+
cos(radians($1[0]))
*
cos(radians($2[0]))
*
cos(radians($2[1])
-
radians($1[1]))
) * 6370.0;
$_$ LANGUAGE sql IMMUTABLE;
Override the distance operator <-> using this function when used with latlongs:
CREATE OPERATOR <-> ( PROCEDURE = orthodromic_distance
, LEFTARG = latlong, RIGHTARG = latlong
);
Now in your SQL queries, to find the nearest entities:
WITH
station_distance AS (
SELECT
id AS station_id,
point(lat, long)::latlong <-> point(6.414478, 12.466646)::latlong AS distance
FROM station_location
WHERE NOT is_deleted
)
SELECT
sl.state_name,
sl.country_name,
sl.locality,
point(sl.lat, sl.long)::latlong AS coordinates,
sd.distance
FROM
station_location sl
JOIN station_distance sd
ON sd.station_id = sl.id
ORDER BY
distance ASC
LIMIT 10
You probably want to store the position lat and long in the same field using the latlong type.

The manual clarifies :
An output column's name can be used to refer to the column's value in
ORDER BY and GROUP BY clauses, but not in the WHERE or HAVING clauses;
there you must write out the expression instead.

Related

column "distance" does not exist

I am trying to run a query that finds the places near my location, but every time I try to run it, it returns the following error:
column "distance" does not exist
If I remove distance and only leave up to the FROM posts, it returns the post id and the distance column.
But if I leave it in the original way, it returns the error.
SELECT id,
( 3959 * acos( cos( radians(-32.63) ) * cos( radians( latitude ) ) *
cos( radians( longitude ) - radians(-71.42) ) + sin( radians(-32.63) ) *
sin( radians( latitude ) ) ) ) AS distance
FROM posts
HAVING distance < 25
ORDER BY distance;
You can't use a column alias in other places in the same query. You'll have to either repeat the entire expression in your HAVING and ORDER BY or use your existing query as a sub-query and apply the HAVING and ORDER BY to the outer query, or use a CTE if your RDBMS supports it.
SELECT id, distance FROM
(SELECT id, ( 3959 * acos( cos( radians(-32.63) ) * cos( radians( latitude ) ) *
cos( radians( longitude ) - radians(-71.42) ) + sin( radians(-32.63) ) *
sin( radians( latitude ) ) ) ) AS distance FROM posts) p
WHERE distance < 25 ORDER BY distance;
you need to put CTA to order it by distance

Google map + places near to current location from SQL Server with haversine formulae

I am trying to display a list of general stores that are near to the current map location.
Everything works fine but I just realised no matter where I pan the map to, a lot of stores that are no where near to the current map location gets call from my SQL Server DB.
Example:
I have a store which resides in Germany in the DB:
Venue Venue_Lat Venue_Lng
Bookstore 51.165712 10.451547
I am using the Haversine formulae in my Stored Procedure [GetNearbyLocations] to retrieve the stores that are suppose to be within 50 miles of the current location, say I pass in a current location as USA with a radius of 50 miles:
Radius: 50
USA Lat: 38.5554745
USA Lng: -95.6649999
The store in Germany gets called as well even though its not within the 50 miles radius. I understand and accept that the formulae is not 100% accurate but is this usual? Can anyone please kindly advice on this. Thanks.
A portion of the SProc:
DECLARE #intMilesModifier int
SET #intMilesModifier = 3959
SELECT .... some fields,
[Venue_Lat],
[Venue_Long],
(#intMilesModifier*acos(cos(radians(#dmlLat))*cos(radians(Venue_Lat))*cos(radians(Venue_Long)-radians(#dmlLng))+sin(radians(#dmlLat))*sin(radians(Venue_Lat)))) AS distance
FROM [Stores] a
WHERE (#intMilesModifier*acos(cos(radians(#dmlLat))*cos(radians(Venue_Lat))*cos(radians(Venue_Long)-radians(#dmlLng))+sin(radians(#dmlLat))*sin(radians(Venue_Lat)))) < #Radius
This PHP/MySQL PDO SQL statement uses the formula (6371 for kilometers,3959 for miles)
$stmt = $dbh->prepare("SELECT name, lat, lng, ( 3959 * acos( cos( radians(?) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(?) ) + sin( radians(?) ) * sin( radians( lat ) ) ) ) AS distance FROM gbstn HAVING distance < ? ORDER BY distance LIMIT 0 , 20");
// Assign parameters
$stmt->bindParam(1,$center_lat);//coordinate of center
$stmt->bindParam(2,$center_lng);//coordinate of center
$stmt->bindParam(3,$center_lat);
$stmt->bindParam(4,$radius);
You will need to modify this to suit
EDIT
You have extra calculations in your formula. I have edited my statement to show what you should have. I removed [] brackets and a from [Stores] a, don't need alias, but cant run query as I dont have SQL Server.
"SELECT Venue_Lat,Venue_Long,(#intMilesModifier * acos( cos( radians(#dmlLat) ) * cos( radians( Venue_Lat ) ) * cos( radians( Venue_Long ) - radians(#dmlLng) ) + sin( radians(#dmlLat) ) * sin( radians( Venue_Lat ) ) ) ) AS distance FROM Stores HAVING distance < #Radius ORDER BY distance LIMIT 0 , 20"

find the nearest location in ms-sql

I send these paramaters my script : Latitude : 41.0186 Longitude : 28.964701 (it is sample). i want to find nearest location's name. how to do this? (query's where code's must be changed)
Sql Query :
SELECT Name FROM Location
WHERE Latitude = 41.0186 AND longitude= 28.964701
Location table likes this: (in real, this is huge table)
Latitude longitude Name
41.0200500000 40.5234490000 a
41.0185714000 37.0975924000 b
41.0184913000 34.0373739000 c
41.0166667000 39.5833333000 d
41.0166667000 28.9333333000 e
Use this function
CREATE FUNCTION dbo.DictanceKM(#lat1 FLOAT, #lat2 FLOAT, #lon1 FLOAT, #lon2 FLOAT)
RETURNS FLOAT
AS
BEGIN
RETURN ACOS(SIN(PI()*#lat1/180.0)*SIN(PI()*#lat2/180.0)+COS(PI()*#lat1/180.0)*COS(PI()*#lat2/180.0)*COS(PI()*#lon2/180.0-PI()*#lon1/180.0))*6371
END
You may order by this function, BUT on large datasets it will be very slow, so try to prefilter the recordset
UPD:
Using #chopikadze's test data:
declare #lat float, #lng float
select #lat = 41.0186, #lng = 28.964701
declare #Location table(Latitude float, Longtitude float, Name nvarchar(50))
insert into #Location(Latitude, Longtitude, Name) values (41.0200500000, 40.5234490000, 'a')
insert into #Location(Latitude, Longtitude, Name) values (41.0185714000, 37.0975924000, 'b')
insert into #Location(Latitude, Longtitude, Name) values (41.0184913000, 34.0373739000, 'c')
insert into #Location(Latitude, Longtitude, Name) values (41.0166667000, 39.5833333000, 'd')
insert into #Location(Latitude, Longtitude, Name) values (41.0166667000, 28.9333333000, 'e')
SELECT ABS(dbo.DictanceKM(#lat, Latitude, #lng, Longtitude)) DistanceKm, * FROM #Location
ORDER BY ABS(dbo.DictanceKM(#lat, Latitude, #lng, Longtitude))
Assuming that the Earth is NOT a geoid, but the round ball, if you need under 1m exact formula - I can find it, don't have it with me
declare #latitude float, #longitude float
select #latitude = 41.0186, #longitude = 28.964701
SELECT [Name] --, other columns
,Distance
from
(
select
[Name] --, other columns
,( 3959 * acos( cos( radians(#latitude) ) * cos( radians( [Lattitude] ) ) * cos( radians( [Longitude] )
- radians(#longitude) ) + sin( radians(#latitude) ) * sin( radians( [Lattitude] ) ) ) )
AS Distance FROM [dbo].[Location]
) as x
where Distance < 5
order by distance
I think this is not so easy as it feels. You must do some trigonometry calculations to get the nearest positsion to your location.
Found nice Javascript example:
var R = 6371; // km Radius of earth
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
It gives you the distance between the points.
Or you can try to pass in a sort order of the squared delta values of the long and lats:
((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) +
(<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN))

How do i call a stored procedure or stored function from sql select statement

I have created following stored user defined it gets executed successfully.
CREATE FUNCTION spherical_distance1(#a float, #b float, #c float , #Lat float, #Lng float)
RETURNS float
AS
BEGIN
RETURN ( 6371 * ACOS( COS( #a/#b ) * COS( #Lat/#b ) * COS( #Lng/#b - #c/#b ) + SIN( #a/#b ) * SIN( #Lat/#b )))
END
The problem i am facing is here, when i call stored function spherical_distance1 ,it shows error like 'spherical_distance1' is not a recognized built-in function name.
SELECT *, spherical_distance1(12.9216667, 57.2958, 77.591667, Lat, Lng) AS distance
FROM business3
WHERE distance < 3
AND StreetName LIKE '%jayanagar %'
AND Keyword LIKE '%plumbing %'
ORDER BY spherical_distance1(12.9216667, 57.2958, 77.591667, Lat, Lng);
In SQL server, you need to prefix function names with the schema.
Most likely, yours will be dbo, so try calling
select *,
dbo.spherical_distance1(12.9216667 ,57.2958,77.591667,Lat ,Lng) as distance
from
business3
where
(( distance < 3 ) and (StreetName like '%jayanagar %') and (Keyword like '%plumbing %' ))
order by
distance -- don't need to repeat the function here
First mistake - it's a USERFUNCTION not a STOREDPROCEUDRE
Second - to call the user function you have to use
SELECT dbo.functionName()
so for your case
SELECT dbo.spherical_distance1(12.9216667, 57.2958, 77.591667, Lat, Lng) AS distance
you need to include "dbo." before function name in your query...

SQL Server AS statement aliased column within WHERE statement

I want to execute a query in which I rename one of the columns using the 'AS' statement and reuse that aliased column name within the 'WHERE' statement. Below is an example:
SELECT lat AS latitude
FROM poi_table
WHERE latitude < 500
The problem here is that SQL Server does not like this query because of the WHERE clause and the AS statement name being referenced in the WHERE clause. Can anyone explain why this is happening and what I can do to remedy my situation?
Suppose I were to have a formula that I have aliased in the SELECT portion of the query, how do I tackle that?
SELECT *,
( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) )
AS distance
FROM poi_table
WHERE distance < 500;
SQL doesn't typically allow you to reference column aliases in WHERE, GROUP BY or HAVING clauses. MySQL does support referencing column aliases in the GROUP BY and HAVING, but I stress that it will cause problems when porting such queries to other databases.
When in doubt, use the actual column name:
SELECT t.lat AS latitude
FROM poi_table t
WHERE t.lat < 500
I added a table alias to make it easier to see what is an actual column vs alias.
Update
A computed column, like the one you see here:
SELECT *,
( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) ) AS distance
FROM poi_table
WHERE distance < 500;
...doesn't change that you can not reference a column alias in the WHERE clause. For that query to work, you'd have to use:
SELECT *,
( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) ) AS distance
FROM poi_table
WHERE ( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) ) < 500;
Be aware that using a function on a column (IE: RADIANS(lat)) will render an index useless, if one exists on the column.
SQL Server is tuned to apply the filters before it applies aliases (because that usually produces faster results).
You could do a nested select statement. Example:
SELECT Latitude FROM
(
SELECT Lat AS Latitude FROM poi_table
) A
WHERE Latitude < 500
I realize this may not be what you are looking for, because it makes your queries much more wordy.
A more succinct approach would be to make a view that wraps your underlying table:
CREATE VIEW vPoi_Table AS
SELECT Lat AS Latitude FROM poi_table
Then you could say:
SELECT Latitude FROM vPoi_Table WHERE Latitude < 500
I am not sure why you cannot use "lat" but, if you must you can rename the columns in a derived table.
select latitude from (SELECT lat AS latitude FROM poi_table) p where latitude < 500
This would work on your edited question !
SELECT * FROM (SELECT <Column_List>,
( 6371*1000 * acos( cos( radians(42.3936868308) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(-72.5277256966) ) + sin( radians(42.3936868308) ) * sin( radians( lat ) ) ) )
AS distance
FROM poi_table) TMP
WHERE distance < 500;
Logical Processing Order of the SELECT statement
The following steps show the logical processing order, or binding
order, for a SELECT statement. This order determines when the objects
defined in one step are made available to the clauses in subsequent
steps. For example, if the query processor can bind to (access) the
tables or views defined in the FROM clause, these objects and their
columns are made available to all subsequent steps. Conversely,
because the SELECT clause is step 8, any column aliases or derived
columns defined in that clause cannot be referenced by preceding
clauses. However, they can be referenced by subsequent clauses such as
the ORDER BY clause. Note that the actual physical execution of the
statement is determined by the query processor and the order may vary
from this list.
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
Source: http://msdn.microsoft.com/en-us/library/ms189499%28v=sql.110%29.aspx
Both accepted answer and Logical Processing Order explain why you could not do what you proposed.
Possible solution:
use derived table (cte/subquery)
use expression in WHERE
create view/computed column
From SQL Server 2008 you could use APPLY operator combined with Table valued Constructor:
SELECT *, s.distance
FROM poi_table
CROSS APPLY (VALUES(6371*1000*acos(cos(radians(42.3936868308))*cos(radians(lat))*cos(radians(lon)-radians(-72.5277256966))+sin(radians(42.3936868308))*sin(radians(lat))))) AS s(distance)
WHERE distance < 500;
LiveDemo
I am not sure why you cannot use "lat" but, if you must you can rename the columns in a derived table.
select a.latitude from (SELECT lat AS latitude FROM poi_table) a where latitude < 500