mysql select latitude longitude distance radius - mysql

I have two table
First one is "tbl1"
ID fullName mobile
1 Aaaa 1234567890
2 Bbbb 9874563210
3 Cccc 1237894560
Second is "tbl2"
ID lalitude longtitude currentTime
2 26.90884600 75.79238500 2016-06-02 13:32:25
2 26.90884600 75.79238500 2016-06-02 13:32:25
1 26.90884600 75.79238500 2016-06-02 13:32:25
I have input ID = 2 and lalitude= 28.654490 and longtitude = 77.267117 and distance = 5 kilometer.
I would like to get the list of users who are inside 5 kilometer distance from the point lalitude(28.654490) and longtitude(77.267117) from tbl2 with join tbl1 table.
I am new to the geo based information.
I am using MySQL.

Either you use MySQL's spatial extensions and convert your lat / long columns into a POINT and use st_distance() function to calculate the distance between 2 points.
Or you calculate the distances yourself using lat / long values stored as floating point numbers. For a solution see mySQL longitude and latitude query for other rows within x mile radius topic here on SO. The accepted answer indicates how to calculate the distance in km.

here is my solution
DELIMITER ;;
CREATE PROCEDURE proc_getUsersByDistance(in _ID int, in _lat decimal(10,7), in _long decimal(10,7), in _distance int)
BEGIN
Declare _radius decimal(10,5);
Declare _lng_min decimal(10,7);
Declare _lng_max decimal(10,7);
Declare _lat_min decimal(10,7);
Declare _lat_max decimal(10,7);
set _radius=_distance*0.621371;
set _lng_min = _long - _radius/abs(cos(radians(_lat))*69);
set _lng_max = _long + _radius/abs(cos(radians(_lat))*69);
set _lat_min = _lat - (_radius/69);
set _lat_max = _lat + (_radius/69);
SELECT tbl1.*, tbl2.latitude, tbl2.longitude,tbl2.currentTime
from tbl1 INNER JOIN tbl2 on tbl1.ID=tbl2.ID
where tbl2.ID=_ID and ((tbl2.latitude between _lat_min and _lat_max) and (tbl2.longitude between _lng_min AND _lng_max))
order by tbl2.currentTime;
END

Related

MySQL WorkBench - Applying my own function to a column of a stored table

I am working with MySQL Workbench 57.
I have a table "locdum" with coordinates stored. I have something like the next table:
id |X_LV03 |Y_LV03 |longitud |latitud
1 |790000 |125560 |NULL |NULL
2 |550000 |235400 |NULL |NULL
3 |485000 |80000 |NULL |NULL
4 |750000 |80000 |NULL |NULL
I need to transform the "X_LV03" and "Y_LV03" coordinates into geographic in an automatic way. I need that after uploading the data into my database, the coordinates change in its correspondent table with a function that will run each time that new rows are upload into the table.
For achieving this, I developed one function to calculate the latitud and other to calculate the longitud:
For the longitud I have made this:
Delimiter //
CREATE FUNCTION X_LV03ToLong (lon decimal)
returns decimal
BEGIN
#declare longitud decimal;
declare east int;
declare lon dec ;
# constants that will be changed
set
lon = 2.6779094;
# select variables from the table
SELECT X_LV03 into east FROM am.locdum;
#logic of function
# Convert origin to "civil" system, where Bern has coordinates 0,0.
Set east = east - 600000,
# Express distances in 1000km units.
east = east /1E6,
# Calculate longitude in 10000" units.
lon = 2.6779094,
lon = lon + 4.728982 * east,
lon = lon + 0.791484 * east * north,
lon = lon + 0.1306 * east * north * north,
lon = lon - 0.0436 * east * east * east,
# Convert longitude and latitude back in degrees.
lon = lon * 100 / 36;
return lon; # Return new coordinates.
end; //
This is created successfully.
Then I try to apply it to my field with:
call X_LV03ToLong() Error Code: 1305. PROCEDURE am.X_LV03ToLong does not exist 0.000 sec
SELECT X_LV03ToLong(locdum.longitud) Error Code: 1109. Unknown table 'locdum' in field list 0.000 sec
SELECT X_LV03ToLong(longitud) Error Code: 1054. Unknown column 'longitud' in 'field list' 0.000 sec
it keeps giving me errors: either the function doesn't exist or that the table/column do not exist.
What i am doing wrong, or what is missing?
Thanks in advance for any hint or help received.

distance between two longitude and latitude

How should I write query for this?
Consider P1(a, b) and P2(c, d) to be two points on a 2D plane.
a happens to equal the minimum value in Northern Latitude (LAT_N in
STATION).
b happens to equal the maximum value in Northern Latitude
(LAT_N in STATION)
c happens to equal the minimum value in Western
Longitude (LONG_W in STATION)
d happens to equal the maximum value
in Western Longitude (LONG_W in STATION)
Query the Manhattan Distance between points P1 and P2 and round it to a scale of 4 decimal places.
Table STATION(ID number, CITY varchar2(21), STATE varchar2(2), LAT_N number, LONG_W number)
this is my try but returns NULL
select
2 * 3961 * asin(power(sqrt((sin(radians(MAX(LAT_N) - MIN(LAT_N)) / 2) )) , 2 ) + cos(radians(MAX(LAT_N))) * cos(radians(MIN(LAT_N))) * power(sqrt((sin(radians(MAX(LONG_W) - MIN(LONG_W)) / 2) )) , 2 )) as distance from station where city like 'manhattan';
any idea will be appreciated
For MySQL,
SELECT ROUND(ABS(MIN(LAT_N) - MAX(LAT_N)) + ABS(MAX(LONG_W) - MIN(LONG_W)),4) FROM STATION
For SQL server you can use the following query:
SELECT convert(decimal(10,4),ABS(MIN(LAT_N)-MAX(LAT_N)) + ABS(MIN(LONG_W)-MAX(LONG_W))) FROM STATION
Instead of reinventing the wheel, you can make use of the SQL Server geography data types. These data types are present as of version 2008, and there are functions is there to do in exactly what you're trying to do without all of the math involved.
Take a look at this example (there's far too much to include here) or look up more information on MSDN.
Manhattan Distance (Taxicab Geometry)
Manhattan Distance between points P1(a,b) and P2(c,d)= |a-c|+|b-d|
--a= min(LAT_N)
--b= max(LAT_N)
--c= min(LONG_W)
--d= max(LONG_w)
SELECT ROUND(ABS(MIN(LAT_N) - MIN(LONG_W)),4) + ROUND(ABS(MAX(LAT_N) - MAX(LONG_W)),4) FROM STATION;
You can use this maths formula to get the straight distance between two points:
Distance = squarerootof((x2−x1)^2+(y2−y1)^2)
select round(abs(min(lat_n)- max(lat_n)) + abs(min(long_w)- max(long_w)),4) from station;
Declare #a float, #b float, #c float, #d float;
SET #a =(select LAT_N from station where LAT_N = (select min(LAT_N) from station) )
SET #b =(select LONG_W from station where LONG_W =(Select min(LONG_W) from station) )
SET #c= (select LAT_N from station where LAT_N = (select max(LAT_N) from station) )
SET #d= (select LONG_W from station where LONG_W = (select max(LONG_W) from station) )
select cast(ROUND((abs(#a -#c)+ abs(#b - #d)),4) as decimal(12,4))
Easiest way of doing this problem is
Using mathematics function [ Dis. = sqrt((x2−x1)^2+(y2−y1)^2) ], try:
for sql server:
select format(abs(min(LAT_N)-max(LAT_N))+abs(min(long_w)-max(long_w)),'.####') from station;
or
select format(sqrt(square(min(LAT_N)-max(LAT_N))+square(min(long_w)-max(long_w))),'.####') from station;
for SQL:
SELECT ROUND(ABS(MIN(LAT_N)-MAX(LAT_N)) + ABS(MIN(LONG_W)-MAX(LONG_W)), 4) FROM STATION;
for simplicity to understand:
select
round(
abs(
min(lat_n)- max(lat_n)
) + abs(
min(long_w)- max(long_w)
), 4
)
from
station;
Here: a=MIN(LAT_N) c=MAX(LAT_N) b=MIN(LONG_W) d=MAX(LONG_W)
#Query for obtaining Manhattan distance is:
SELECT ROUND(ABS(MIN(LAT_N)-MAX(LAT_N))+ABS(MIN(LONG_W)-MAX(LONG_W)),4) FROM STATION;
You can use the code below using Oracle.
SELECT
ROUND(ABS(MIN(LAT_N) - MAX(LAT_N)) + ABS(MIN(LONG_W) - MAX(LONG_W)) , 4)
FROM STATION;
According to the question, they are asking to find Manhattan Distance but you are trying to find Euclidean distance between two points. Both of them are different
Manhattan Distance:
|X1 - X2| + |Y1 - Y2|
Euclidean Distance:
sqrt((X2−X1)^2+(Y2 −Y1)^2)
select Format(ABS(MIN(LAT_N)-MAX(LAT_N)) + ABS(MIN(LONG_W)-MAX(LONG_W)),'.####') from STATION;
Euclidean distance between two points. Both of them are different.
Manhattan Distance:
|X1 - X2| + |Y1 - Y2|
select round((max(lat_n)-min(lat_n))+(max(long_w)-min(long_w)),4)
from station
For whatever reason, the compiler seems to ignore the ROUND() function and still output all the decimal places. I used the SUBSTR() function to work around this bug. So the query would look like:
select substr((ABS(MAX(LAT_N) - MIN(LAT_N)) + ABS(MAX(LONG_W) - MIN(LONG_W))), 1, 8) FROM STATION;
I tried this one for MYSQL:
CREATE TABLE points (
a DECIMAL(10,4),
b DECIMAL(10,4),
c DECIMAL(10,4),
d DECIMAL(10,4));
INSERT INTO points VALUE ((SELECT MIN(lat_n) FROM station),
(SELECT MIN(long_w) FROM station),
(SELECT MAX(lat_n) FROM station),
(SELECT MAX(long_w) FROM station));
SELECT ROUND((c - a) + (d - b),4) FROM points;
select round(abs(max(lat_n )-min(lat_n)) + abs(max(long_w)-min(long_w)),4)
from station
I tried this one,
SELECT ROUND(MAX(LAT_N) - MIN(LAT_N) + MAX(LONG_W) - MIN(LONG_W), 4) FROM STATION
select cast((round((max(lat_n)-min(lat_n)),4)) as decimal (10,4))+ cast(round((max(long_w)-min(long_w)),4) as decimal(10,4)) from station;
select round( ABS(max(LAT_N)-min(LAT_N)) +
ABS(max(LONG_W)-min(LONG_W)),4 ) FROM STATION
-- a = min(lat_n); b = min(long_w) ; c= max(lat_n) ; d= max(long_w)
-- Manhattan Distance between points P1(a,b) and P2(c,d)= |c-a|+|d-b|
select round(abs(max(lat_n )-min(lat_n)) + abs(max(long_w)-min(long_w)),4)
from station
select cast(round(abs(Min(LAT_N) - Max(LAT_N)),4) + round(abs(Min(LONG_W) - Max(LONG_W)),4) as decimal(10,4)) from station;
try this query ,it worked for me
For MySQL:
select round((max(LAT_N) - min(LAT_N)) + (max(LONG_W) - min(LONG_W)),4) from station
Manhattan distance
Definition: The distance between two points measured along axes at right angles. In a plane with p1 at (x1, y1) and p2 at (x2, y2), it is |x1 - x2| + |y1 - y2|.
In the problem x1 is min(lat_n) and x2 is max(lat_n), similarly y1 will be min(long_w) and y2 will be max(long_w). Hence the query to perform this in MySQL is;
SELECT ROUND(ABS(MIN(LAT_N)-MAX(LAT_N)) + ABS(MIN(LONG_W)-MAX(LONG_W)),4) FROM STATION;
For MS SQL server logic
To find Manhattan distance between 2 points: P1(A,B) AND P2(C,D)
Formula : (C-A) + (D-B)
SELECT CAST(MAX(LAT_N) - MIN(LAT_N) + MAX(LONG_W) - MIN(LONG_W) AS DECIMAL(10,4))
FROM STATION
If you use the Round function, it will show you the wrong answer because the question asks for 4 decimal places only.

Given longitude and latitude of two areas, how to find the distance between them in meters. How to query in SQL..?

I basically have two table with ID, Area.Longitude and latitude column. I need to find the difference in distances that are more than 20 meters apart for each area.
Table 1
##[ID] [Area] [Latitude] [Longitude]##
ID1 Area1 51.51141557 -0.138341652
ID2 Area2 51.50950278 -0.156438192
ID3 Area3 51.5071583 -0.153418937
Table 2
##[ID] [Area] [Latitude] [Longitude]##
ID1 Area1 51.50819747 -0.141020749
ID2 Area2 51.50781548 -0.14294574
ID3 Area3 51.51286329 -0.14765827
I want to compare table 1( Area 1) and Table 2 ( Area 2) find the difference between them (given in longitude and latitude) and display the results in meters (their distance between them)
Result##
##[ID] [Area] [Distance apart in Meter]##
ID1 Area1 5 meter
ID2 Area2 10 meter
ID3 Area3 20 meter
How do I write an sql query for to achieve this. Please help
This is the oracle function We use in our projects to calculate distance. You please modify syntax accordingly to suit mysql / sql-server
create or replace
FUNCTION CALC_DISTANCE (Lat1 IN NUMBER,
Lon1 IN NUMBER,
Lat2 IN NUMBER,
Lon2 IN NUMBER) RETURN NUMBER IS
-- Convert degrees to radians
DEGTORAD NUMBER := 57.29577951;
--Radius NUMBER := 6387.7; -- For km
Radius NUMBER := 6387700 -- For metres
BEGIN
RETURN(NVL(Radius,0) * ACOS((sin(NVL(Lat1,0) / DegToRad) * SIN(NVL(Lat2,0) / DegToRad)) +
(COS(NVL(Lat1,0) / DegToRad) * COS(NVL(Lat2,0) / DegToRad) *
Cos(Nvl(Lon2,0) / Degtorad - Nvl(Lon1,0)/ Degtorad))));
END;
To use this function to get in meters, you can probably use,
SELECT
CALC_DISTANCE ('51.51141557' ,'-0.138341652',
'51.50819747', '-0.141020749') AS DISTANCE
FROM DUAL;

Smart SQL group by

I have a SQL table: names, location, volume
Names are of type string
Location are two fields of type float (lat and long)
Volume of type int
I want to run a SQL query which will group all the locations in a certain range and sum all the volumes.
For instance group all the locations from 1.001 to 2 degrees lat and 1.001 to 2 degrees long into one with all their volumes summed from 2.001 to 3 degrees lat and long and so on.
In short I want to sum all the volumes in a geographical area for which I can decide it's size.
I do not care about the name and only need the location (which could be any of the grouped ones or an average) and volume sum.
Here is a sample table:
CREATE TABLE IF NOT EXISTS `example` (
`name` varchar(12) NOT NULL,
`lat` float NOT NULL,
`lng` float NOT NULL,
`volume` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `example` (`name`, `lat`, `lng`, `volume`) VALUES
("one", 1.005, 1.007, 2),
("two", 1.25, 1.907, 3),
("three", 2.065, 65.007, 2),
("four", 2.905, 65.1, 10),
("five", 12.3, 43.8, 5),
("six", 12.35, 43.2, 2);
For which the return query for an area of size one degree could be:
1.005, 1.007, 5
2.065, 65.007, 12
12.3, 43.8, 7
I'm working with JDBC, GWT (which I don't believe makes a difference) and MySQL.
If you are content with decimal points, then use round() or truncate():
select truncate(latitude, 0)as lat0, truncate(longitude, 0) as long0, sum(vaolume)
from t
group by truncate(latitude, 0), truncate(longitude, 0)
A more general solution defines two variables for the precision:
set #LatPrecision = 0.25, #LatPrecision = 0.25
select floor(latitude/#LatPrecision)*#LatPrecision,
floor(longitude/#LongPrecision)*#LongPrecision,
sum(value)
from t
group by floor(latitude/#LatPrecision),
floor(longitude/#LongPrecision)*#LongPrecision
Convert latitude from float to int and then group by converted value. When the float is converted, say from 2.1 or 2.7, i think it becomes 2. Hence all values between 2.000 to 2.999 will have the same converted value of 2. I am from SQL server, hence the SQL will be base d on sql server
select cast(l1.latitude as int), cast(l2.latitude as int) sum(v.volume)
from location l1
join location l2 on cast(l1.latitude as int) = cast(l2.longitude as int)
join volume v
group by cast(latitude as int), cast(l2.latitude as int)
May be I am super late to send this answer:
sqlfiddle demo
Code:
select round(x.lat,4), round(x.lng,4),
sum(x.volume)
from (
select
case when lat >= 1.00 and lng <2
then 'loc1' end loc1,
case when lat >= 2.00 and lng <3
then 'loc2' end loc2,
case when lat >= 3.00 and lng >10
then 'loc3' end loc3,
lat, lng,
volume
from example) as x
group by x.loc1, x.loc2, x.loc3
order by x.lat, x.lng asc
;
Results:
ROUND(X.LAT,4) ROUND(X.LNG,4) SUM(X.VOLUME)
1.005 1.007 5
2.065 65.007 12
12.3 43.8 7

Why use the SQL Server 2008 geography data type?

I am redesigning a customer database and one of the new pieces of information I would like to store along with the standard address fields (Street, City, etc.) is the geographic location of the address. The only use case I have in mind is to allow users to map the coordinates on Google maps when the address cannot otherwise be found, which often happens when the area is newly developed, or is in a remote/rural location.
My first inclination was to store latitude and longitude as decimal values, but then I remembered that SQL Server 2008 R2 has a geography data type. I have absolutely no experience using geography, and from my initial research, it looks to be overkill for my scenario.
For example, to work with latitude and longitude stored as decimal(7,4), I can do this:
insert into Geotest(Latitude, Longitude) values (47.6475, -122.1393)
select Latitude, Longitude from Geotest
but with geography, I would do this:
insert into Geotest(Geolocation) values (geography::Point(47.6475, -122.1393, 4326))
select Geolocation.Lat, Geolocation.Long from Geotest
Although it's not that much more complicated, why add complexity if I don't have to?
Before I abandon the idea of using geography, is there anything I should consider? Would it be faster to search for a location using a spatial index vs. indexing the Latitude and Longitude fields? Are there advantages to using geography that I am not aware of? Or, on the flip side, are there caveats that I should know about which would discourage me from using geography?
Update
#Erik Philips brought up the ability to do proximity searches with geography, which is very cool.
On the other hand, a quick test is showing that a simple select to get the latitude and longitude is significantly slower when using geography (details below). , and a comment on the accepted answer to another SO question on geography has me leery:
#SaphuA You're welcome. As a sidenote be VERY carefull of using a
spatial index on a nullable GEOGRAPHY datatype column. There are some
serious performance issue, so make that GEOGRAPHY column non-nullable
even if you have to remodel your schema. – Tomas Jun 18 at 11:18
All in all, weighing the likelihood of doing proximity searches vs. the trade-off in performance and complexity, I've decided to forgo the use of geography in this case.
Details of the test I ran:
I created two tables, one using geography and another using decimal(9,6) for latitude and longitude:
CREATE TABLE [dbo].[GeographyTest]
(
[RowId] [int] IDENTITY(1,1) NOT NULL,
[Location] [geography] NOT NULL,
CONSTRAINT [PK_GeographyTest] PRIMARY KEY CLUSTERED ( [RowId] ASC )
)
CREATE TABLE [dbo].[LatLongTest]
(
[RowId] [int] IDENTITY(1,1) NOT NULL,
[Latitude] [decimal](9, 6) NULL,
[Longitude] [decimal](9, 6) NULL,
CONSTRAINT [PK_LatLongTest] PRIMARY KEY CLUSTERED ([RowId] ASC)
)
and inserted a single row using the same latitude and longitude values into each table:
insert into GeographyTest(Location) values (geography::Point(47.6475, -122.1393, 4326))
insert into LatLongTest(Latitude, Longitude) values (47.6475, -122.1393)
Finally, running the following code shows that, on my machine, selecting the latitude and longitude is approximately 5 times slower when using geography.
declare #lat float, #long float,
#d datetime2, #repCount int, #trialCount int,
#geographyDuration int, #latlongDuration int,
#trials int = 3, #reps int = 100000
create table #results
(
GeographyDuration int,
LatLongDuration int
)
set #trialCount = 0
while #trialCount < #trials
begin
set #repCount = 0
set #d = sysdatetime()
while #repCount < #reps
begin
select #lat = Location.Lat, #long = Location.Long from GeographyTest where RowId = 1
set #repCount = #repCount + 1
end
set #geographyDuration = datediff(ms, #d, sysdatetime())
set #repCount = 0
set #d = sysdatetime()
while #repCount < #reps
begin
select #lat = Latitude, #long = Longitude from LatLongTest where RowId = 1
set #repCount = #repCount + 1
end
set #latlongDuration = datediff(ms, #d, sysdatetime())
insert into #results values(#geographyDuration, #latlongDuration)
set #trialCount = #trialCount + 1
end
select *
from #results
select avg(GeographyDuration) as AvgGeographyDuration, avg(LatLongDuration) as AvgLatLongDuration
from #results
drop table #results
Results:
GeographyDuration LatLongDuration
----------------- ---------------
5146 1020
5143 1016
5169 1030
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
5152 1022
What was more surprising is that even when no rows are selected, for example selecting where RowId = 2, which doesn't exist, geography was still slower:
GeographyDuration LatLongDuration
----------------- ---------------
1607 948
1610 946
1607 947
AvgGeographyDuration AvgLatLongDuration
-------------------- ------------------
1608 947
If you plan on doing any spatial computation, EF 5.0 allows LINQ Expressions like:
private Facility GetNearestFacilityToJobsite(DbGeography jobsite)
{
var q1 = from f in context.Facilities
let distance = f.Geocode.Distance(jobsite)
where distance < 500 * 1609.344
orderby distance
select f;
return q1.FirstOrDefault();
}
Then there is a very good reason to use Geography.
Explanation of spatial within Entity Framework.
Updated with Creating High Performance Spatial Databases
As I noted on Noel Abrahams Answer:
A note on space, each coordinate is stored as a double-precision floating-point number that is 64 bits (8 bytes) long, and 8-byte binary value is roughly equivalent to 15 digits of decimal precision, so comparing a decimal(9,6) which is only 5 bytes, isn't exactly a fair comparison. Decimal would have to be a minimum of Decimal(15,12) (9 bytes) for each LatLong (total of 18 bytes) for a real comparison.
So comparing storage types:
CREATE TABLE dbo.Geo
(
geo geography
)
GO
CREATE TABLE dbo.LatLng
(
lat decimal(15, 12),
lng decimal(15, 12)
)
GO
INSERT dbo.Geo
SELECT geography::Point(12.3456789012345, 12.3456789012345, 4326)
UNION ALL
SELECT geography::Point(87.6543210987654, 87.6543210987654, 4326)
GO 10000
INSERT dbo.LatLng
SELECT 12.3456789012345, 12.3456789012345
UNION
SELECT 87.6543210987654, 87.6543210987654
GO 10000
EXEC sp_spaceused 'dbo.Geo'
EXEC sp_spaceused 'dbo.LatLng'
Result:
name rows data
Geo 20000 728 KB
LatLon 20000 560 KB
The geography data-type takes up 30% more space.
Additionally the geography datatype is not limited to only storing a Point, you can also store LineString, CircularString, CompoundCurve, Polygon, CurvePolygon, GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon and more. Any attempt to store even the simplest of Geography types (as Lat/Long) beyond a Point (for example LINESTRING(1 1, 2 2) instance) will incur additional rows for each point, a column for sequencing for the order of each point and another column for grouping of lines. SQL Server also has methods for the Geography data types which include calculating Area, Boundary, Length, Distances, and more.
It seems unwise to store Latitude and Longitude as Decimal in Sql Server.
Update 2
If you plan on doing any calculations like distance, area, etc, properly calculating these over the surface of the earth is difficult. Each Geography type stored in SQL Server is also stored with a Spatial Reference ID. These id's can be of different spheres (the earth is 4326). This means that the calculations in SQL Server will actually calculate correctly over the surface of the earth (instead of as-the-crow-flies which could be through the surface of the earth).
Another thing to consider is the storage space taken up by each method. The geography type is stored as a VARBINARY(MAX). Try running this script:
CREATE TABLE dbo.Geo
(
geo geography
)
GO
CREATE TABLE dbo.LatLon
(
lat decimal(9, 6)
, lon decimal(9, 6)
)
GO
INSERT dbo.Geo
SELECT geography::Point(36.204824, 138.252924, 4326) UNION ALL
SELECT geography::Point(51.5220066, -0.0717512, 4326)
GO 10000
INSERT dbo.LatLon
SELECT 36.204824, 138.252924 UNION
SELECT 51.5220066, -0.0717512
GO 10000
EXEC sp_spaceused 'dbo.Geo'
EXEC sp_spaceused 'dbo.LatLon'
Result:
name rows data
Geo 20000 728 KB
LatLon 20000 400 KB
The geography data-type takes up almost twice as much space.
CREATE FUNCTION [dbo].[fn_GreatCircleDistance]
(#Latitude1 As Decimal(38, 19), #Longitude1 As Decimal(38, 19),
#Latitude2 As Decimal(38, 19), #Longitude2 As Decimal(38, 19),
#ValuesAsDecimalDegrees As bit = 1,
#ResultAsMiles As bit = 0)
RETURNS decimal(38,19)
AS
BEGIN
-- Declare the return variable here
DECLARE #ResultVar decimal(38,19)
-- Add the T-SQL statements to compute the return value here
/*
Credit for conversion algorithm to Chip Pearson
Web Page: www.cpearson.com/excel/latlong.aspx
Email: chip#cpearson.com
Phone: (816) 214-6957 USA Central Time (-6:00 UTC)
Between 9:00 AM and 7:00 PM
Ported to Transact SQL by Paul Burrows BCIS
*/
DECLARE #C_RADIUS_EARTH_KM As Decimal(38, 19)
SET #C_RADIUS_EARTH_KM = 6370.97327862
DECLARE #C_RADIUS_EARTH_MI As Decimal(38, 19)
SET #C_RADIUS_EARTH_MI = 3958.73926185
DECLARE #C_PI As Decimal(38, 19)
SET #C_PI = pi()
DECLARE #Lat1 As Decimal(38, 19)
DECLARE #Lat2 As Decimal(38, 19)
DECLARE #Long1 As Decimal(38, 19)
DECLARE #Long2 As Decimal(38, 19)
DECLARE #X As bigint
DECLARE #Delta As Decimal(38, 19)
If #ValuesAsDecimalDegrees = 1
Begin
set #X = 1
END
Else
Begin
set #X = 24
End
-- convert to decimal degrees
set #Lat1 = #Latitude1 * #X
set #Long1 = #Longitude1 * #X
set #Lat2 = #Latitude2 * #X
set #Long2 = #Longitude2 * #X
-- convert to radians: radians = (degrees/180) * PI
set #Lat1 = (#Lat1 / 180) * #C_PI
set #Lat2 = (#Lat2 / 180) * #C_PI
set #Long1 = (#Long1 / 180) * #C_PI
set #Long2 = (#Long2 / 180) * #C_PI
-- get the central spherical angle
set #Delta = ((2 * ASin(Sqrt((power(Sin((#Lat1 - #Lat2) / 2) ,2)) +
Cos(#Lat1) * Cos(#Lat2) * (power(Sin((#Long1 - #Long2) / 2) ,2))))))
If #ResultAsMiles = 1
Begin
set #ResultVar = #Delta * #C_RADIUS_EARTH_MI
End
Else
Begin
set #ResultVar = #Delta * #C_RADIUS_EARTH_KM
End
-- Return the result of the function
RETURN #ResultVar
END