Sql server 如何编写一个TVF,它接受3个参数并返回一个表,其中包含与另一个表中的条件匹配的行的ID
我是表值函数的新手,在开始使用这个函数时遇到了困难。 我正在尝试创建一个接受3的函数parameters@Lat十进制,@长十进制,@RangeInMiles int。 我要做的是从Locations表返回位置ID,该表位于传递的参数@RangeInMiles的半径范围内 位置表包括以下列: loc_ID PK标识 典型的地址信息 经度 位置纬度 因此,我们的想法是让这个函数使用传递的lat和long与Locations表中的lat和long进行比较,并返回传递半径内的id:@RangeInMiles 到目前为止,我掌握的所有代码都在下面,但WHERE子句让我感到困惑。谢谢你的帮助Sql server 如何编写一个TVF,它接受3个参数并返回一个表,其中包含与另一个表中的条件匹配的行的ID,sql-server,user-defined-functions,Sql Server,User Defined Functions,我是表值函数的新手,在开始使用这个函数时遇到了困难。 我正在尝试创建一个接受3的函数parameters@Lat十进制,@长十进制,@RangeInMiles int。 我要做的是从Locations表返回位置ID,该表位于传递的参数@RangeInMiles的半径范围内 位置表包括以下列: loc_ID PK标识 典型的地址信息 经度 位置纬度 因此,我们的想法是让这个函数使用传递的lat和long与Locations表中的lat和long进行比较,并返回传递半径内的id:@RangeInMi
CREATE FUNCTION dbo.GetAvailableSalesByDistance(@Lat decimal, @Long decimal, @RangeInMiles int)
RETURNS @LocationIds Table
(loc_ID int)
AS
BEGIN INSERT @LocationIds
SELECT Locations.loc_ID
FROM Locations
WHERE
RETURN
END
你可以试试下面的毕达哥拉斯:
CREATE FUNCTION dbo.GetAvailableSalesByDistance(@Lat decimal, @Long decimal, @RangeInMiles int)
RETURNS @LocationIds Table
(loc_ID int)
AS
BEGIN INSERT INTO@LocationIds
SELECT Locations.loc_ID
FROM Locations as l
WHERE (SQRT(SQUARE(l.loc_latitude-@Lat)+SQUARE(l.loc_longitude-@Long))<=@RangeInMiles)
RETURN
END
编辑:
要正确使用,您可以使用haversine:
CREATE FUNCTION dbo.GetAvailableSalesByDistance(@Lat decimal, @Long decimal, @RangeInMiles int)
RETURNS @LocationIds Table
(loc_ID int)
AS
BEGIN INSERT INTO@LocationIds
SELECT Locations.loc_ID
FROM Locations as l
WHERE
(2 * 3961 * asin(sqrt((sin(radians((l.loc_latitude - @Lat) / 2))) ^ 2 + cos(radians(@Lat)) * cos(radians(l.loc_latitude)) * (sin(radians((l.loc_longitude - @Long) / 2))) ^ 2))) <=@RangeInMiles)
RETURN
END
它未经测试,来自此您的insert语句和updated子句可能是:
INSERT @LocationIds
SELECT
Locations.loc_ID
FROM
Locations
WHERE
SQRT(SQUARE(loc_longitude - @Long) + SQUARE(loc_latitude - @Lat)) <= @RangeInMiles
这比@Nikolaus发布的要复杂得多,纬度和经度是在一个地球球体上,不以英里表示,所以毕达哥拉斯的三元组在这里不适用。下面,我们将在SQL Server中获得以下函数来计算距离:
CREATE FUNCTION dbo.spatialDistance(@lat1 DECIMAL, @lon1 DECIMAL, @lat2 DECIMAL, @lon2 DECIMAL)
RETURNS TABLE
AS
RETURN (
SELECT 6371 * Calc2.c AS DistanceKm
, 3958.8 * Calc2.c AS DistanceMi
FROM (SELECT RADIANS(@lat1) AS Lat1
, RADIANS(@lat2) AS Lat2
, RADIANS(@lat2 - @lat1) AS dLat
, RADIANS(@lon2 - @lon1) AS dLon) conv
CROSS
APPLY (SELECT a = SQUARE(SIN(dLat/2.0)) + SQUARE(SIN(dLon/2.0)) * COS(Lat1) * COS(Lat2)) Calc1
CROSS
APPLY (SELECT c = 2 * ATN2(SQRT(Calc1.a), SQRT(1 - Calc1.a))) AS Calc2
)
然后,您可以编写所需的函数,如下所示:
CREATE FUNCTION dbo.GetAvailableSalesByDistance(@Lat decimal, @Long decimal, @RangeInMiles int)
RETURNS Table
AS
RETURN (
SELECT l.loc_id
FROM Locations AS l
CROSS
APPLY dbo.spatialDistance(l.loc_latitude, l.loc_longitude, @Lat, @Long) dist
WHERE dist.DistanceMi <= @RangeInMiles
)
注意,我使用的是内联表值函数。像您在问题中使用的标量函数和多语句TVF对性能很糟糕,不应该使用 看来你需要毕达哥拉斯。三角形函数。谢谢你@Nikolaus。我尝试了您的方法,在调用函数时遇到以下错误:将数据类型nvarchar转换为数字时出错。这是我的电话:从GetAvailablesAlessByDistance34.10、84.52中选择*,5@JP1有可能是loc_latidude或/和loc_longidude属于nvarchar类型吗?你说得对,他们是!对于这个函数,哪一个更好:在“更改函数本身”的“位置”表中切换Lat和Long的数据类型?通常,它也应该以这种方式工作,但可能您有不能转换为十进制的数据。就像两列中的一列中的字母一样。@JP1如果您有无效数据,您可以尝试查找。谢谢您的帮助!这是有道理的。我仍然得到一个错误,当我运行函数调用时,它无法将varchar转换为numeric。思想?将数据类型nvarchar转换为数字时出错。这是我的电话:从GetAvailablesAlessByDistance34.10、84.52中选择*,5@JP1如何定义位置表?位置纬度和位置经度是nvarchar?如果是这样的话,试试@Nikolaus发布的CONVERT查询应该会显示这些信息。在创建我的版本之前,您可能忘记删除GetAvailablesAlessByDistance的早期版本?是的,它们是nvarchar。我必须找到一种方法将这些列转换为数字,这似乎是真的。再次感谢您的支持help@JP1他们应该自动转换,有些只是失败。您是否尝试过从TRY_CONVERTDECIMAL,loc_latitude为空或TRY_CONVERTDECIMAL,loc_latitude为空的位置选择*?这应该列出所有无法转换的记录。也许用逗号代替点作为小数点分隔符?
CREATE FUNCTION dbo.GetAvailableSalesByDistance(@Lat decimal, @Long decimal, @RangeInMiles int)
RETURNS Table
AS
RETURN (
SELECT l.loc_id
FROM Locations AS l
CROSS
APPLY dbo.spatialDistance(l.loc_latitude, l.loc_longitude, @Lat, @Long) dist
WHERE dist.DistanceMi <= @RangeInMiles
)