我在 C# 和 TSQL 中都有一个 Harversine 公式的实现。我不确定如何最好地在服务器端实现公式,以便我能够在 Linq 查询中使用它。
理想情况下,我只需将本地公式链接到服务器上的函数。从而避免“没有转换为 sql”错误,并让一切都变得美好而无缝。
显然,对这个问题的任何看法都是有帮助的。
我知道 SQL2008 中的地理类型。但是,我正在处理的代码库已经对 Linq to SQL 如此依赖,我希望这比它值得付出的努力更多!
谢谢
我在 C# 和 TSQL 中都有一个 Harversine 公式的实现。我不确定如何最好地在服务器端实现公式,以便我能够在 Linq 查询中使用它。
理想情况下,我只需将本地公式链接到服务器上的函数。从而避免“没有转换为 sql”错误,并让一切都变得美好而无缝。
显然,对这个问题的任何看法都是有帮助的。
我知道 SQL2008 中的地理类型。但是,我正在处理的代码库已经对 Linq to SQL 如此依赖,我希望这比它值得付出的努力更多!
谢谢
为什么不使用 100% SQL,因为它是进行计算的最佳方式,并且只需得到一个已经填满距离的表?
从现有答案
CREATE FUNCTION dbo.udf_Haversine(@lat1 float, @long1 float, @lat2 float, @long2 float) RETURNS float
BEGIN
DECLARE @dlon float, @dlat float, @rlat1 float, @rlat2 float, @rlong1 float, @rlong2 float, @a float, @c float, @R float, @d float, @DtoR float
SELECT @DtoR = 0.017453293
SELECT @R = 3937 --3976
SELECT
@rlat1 = @lat1 * @DtoR,
@rlong1 = @long1 * @DtoR,
@rlat2 = @lat2 * @DtoR,
@rlong2 = @long2 * @DtoR
SELECT
@dlon = @rlong1 - @rlong2,
@dlat = @rlat1 - @rlat2
SELECT @a = power(sin(@dlat/2), 2) + cos(@rlat1) * cos(@rlat2) * power(sin(@dlon/2), 2)
SELECT @c = 2 * atn2(sqrt(@a), sqrt(1-@a))
SELECT @d = @R * @c
RETURN @d
END
并使用如下:
var table = from r in db.VenuePostCodes
select new {
lat = r.Latitude,
lng = r.Longitude,
name = r.Name,
distance = db.udf_Haversine(
r.Latitude,r.Longitude,
r.Latitude,r.Longitude2)
};
但最好的办法是始终将所有内容都放在 SQL 上,这样您的托管服务器就不用做太多事情了,只需在 SQL 中添加一个 VIEW 并调用该视图,让我们想象一下:
SELECT
latitude, longitude, name, latitude1, longitude2, postcode,
udf_Haversine(latitude, longitude, latitude2, longitude2) AS distance
FROM
venuepostcodes
ORDER BY
distance
并使用 LINQ 直接调用该视图。
@balexandre 的回答很好,但我对提供的 SQL 函数不满意(它缺少注释,有趣的注释是否不变,是英里吗?公里?等等......)
CREATE FUNCTION [dbo].[udf_Haversine](@lat1 float, @long1 float, @lat2 float, @long2 float) RETURNS float
BEGIN
DECLARE @dlon float, @dlat float,
@rlat1 float, @rlat2 float, @rlong1 float, @rlong2 float,
@a float, @c float, @R float, @d float, @DtoR float
SELECT
@DtoR = PI() / 180, -- Degrees to radians const
@R = 6371 -- Radius of Earth in KM
SELECT
@rlat1 = @lat1 * @DtoR,
@rlong1 = @long1 * @DtoR,
@rlat2 = @lat2 * @DtoR,
@rlong2 = @long2 * @DtoR
SELECT
@dlat = @rlat1 - @rlat2,
@dlon = @rlong1 - @rlong2
SELECT @a = SIN(@dlat / 2) * SIN(@dlat / 2) +
SIN(@dlon / 2) * SIN(@dlon / 2) * COS(@rlat2) * COS(@rlat1)
SELECT @c = 2 * atn2(sqrt(@a), sqrt(1 - @a))
SELECT @d = @R * @c -- Final distance in KM
SELECT @d = @d * 0.621371192 -- Final distance in miles
RETURN @d
END
它是从我们从这里获取的 JavaScript 实现转换而来的,最后还转换为英里:
// Converted from JavaScript implementation:
// http://www.movable-type.co.uk/scripts/latlong.html
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;