我有一个表格,我正在存储Lat / Long坐标,我想查询一下我想要获取距离某一点的所有记录。
此表有大约1000万条记录,并且有一个Lat / Long字段的索引
这不需要精确。除此之外,我正在考虑1度长== 1度Lat,我知道这不是真的,但我得到的椭圆对于这个目的来说足够好了。
对于我下面的例子,假设有问题的点是[40,140],我的半径(以度为单位)是2度。
我尝试了这两种方式:
1)我创建了一个UDF来计算2点之间距离的平方,我在查询中运行该UDF。
SELECT Lat, Long FROM Table
WHERE (Lat BETWEEN 38 AND 42)
AND (Long BETWEEN 138 AND 142)
AND dbo.SquareDistance(Lat, Long, 40, 140) < 4
我首先按方格过滤,以加快查询速度,让SQL使用索引,然后将其精炼为仅匹配圆圈内的记录与我的UDF。
2)运行查询以获取方块(与之前相同,但没有最后一行),将所有这些记录提供给我的ASP.Net代码,并计算ASP.Net端的圆(同样的想法,计算保存Sqrt调用的距离的平方,并与我的半径的平方比较。
令我惊讶的是,计算.Net方面的圆圈的速度比使用UDF快10倍,这让我相信我正在做一些可怕的UDF错误...
这是我正在使用的代码:
CREATE FUNCTION [dbo].[SquareDistance]
(@Lat1 float, @Long1 float, @Lat2 float, @Long2 float)
RETURNS float
AS
BEGIN
-- Declare the return variable here
DECLARE @Result float
DECLARE @LatDiff float, @LongDiff float
SELECT @LatDiff = @Lat1 - @Lat2
SELECT @LongDiff = @Long1 - @Long2
SELECT @Result = (@LatDiff * @LatDiff) + (@LongDiff * @LongDiff)
-- Return the result of the function
RETURN @Result
END
我在这里遗漏了什么吗? 不应该在SQL Server中使用UDF比使用.Net所需的记录多25%以上,以及DataReader的开销,进程之间的通信以及诸如此类的东西?
UDF中是否存在使其运行缓慢的可怕错误? 有没有办法改善它?
非常感谢!
答案 0 :(得分:3)
您可以通过NOT声明变量并更直接地进行计算来提高此UDF的性能。这可能会稍微提高性能,但(但可能不多)。
CREATE FUNCTION [dbo].[SquareDistance]
(@Lat1 float, @Long1 float, @Lat2 float, @Long2 float)
RETURNS float
AS
BEGIN
Return ( SELECT ((@Lat1 - @Lat2) * (@Lat1 - @Lat2)) + ((@Long1 - @Long2) * (@Long1 - @Long2)))
END
更好的方法是删除函数并将计算放在原始查询中。
SELECT Lat, Long FROM Table
WHERE (Lat BETWEEN 38 AND 42)
AND (Long BETWEEN 138 AND 142)
AND ((Lat - 40) * (Lat - 40)) + ((Long - 140) * (Long - 140)) < 4
调用用户定义的函数会产生一些开销。通过删除该功能,您可能会获得一点性能。
另外,我建议您检查一下执行计划,以确保获得预期的索引。
答案 1 :(得分:3)
使用UDF时有lot of overhead。
即使在线编码也可能不太好,因为无法使用索引,尽管BETWEEN条款应该减少需要处理的数据。
为了扩展G Mastros的想法,将选择位与方形位分开。它可能有助于优化者。
SELECT
Lat, Long
FROM
(
SELECT
Lat, Long
FROM
Table
WHERE
(Lat BETWEEN 38 AND 42)
AND
(Long BETWEEN 138 AND 142)
) foo
WHERE
((Lat - 40) * (Lat - 40)) + ((Long - 140) * (Long - 140)) < 4
编辑:您可以减少所涉及的实际计算。 下一个想法可以将计算的数量从7减少到5
...
SELECT
Lat, Long,
Lat - 40 AS LatDiff, Long - 140 AS LongDiff
FROM
...
(LatDiff * LatDiff) + (LongDiff * LongDiff) < 4
...
基本上,尝试提供的3个解决方案,看看哪些有效。 优化器可能会忽略派生表,它可能会使用它,或者它可能会生成更糟糕的计划。
答案 2 :(得分:1)
查看this文章,该文章描述了为什么SQL Server中的UDF通常是个坏主意。除非你非常确定你调用UDF的表不会长大,所以要注意UDF函数总是在表中的所有行上调用,而不是(只能错误地猜测)在结果集上。这可以在数据库增长时给您带来巨大的性能损失。
非常好的文章链接细节也有一些方法可以克服这个问题,但事实上,SQL Server TSQL方言错过了创建标量函数或确定函数的方法(就像Oracle一样)。
答案 3 :(得分:0)
更新
GMastros:你是对的。在查询中进行数学运算比UDF快得多。我正在使用SQUARE()函数进行乘法运算,这使得它更简洁,但性能却相同。然而,这样做是仍然比在.Net中进行数学运算慢两倍。
我无法理解这一点,但我已经达成了对我的特定情况有用的妥协(这很糟糕,因为我需要复制代码,但这是最好的方案,除非我们能找到一种方法来制作圆圈SQL中的计算更快)
谢谢!