【问题标题】:SQL server Coordinate distance optimizationSQL server 坐标距离优化
【发布时间】:2016-04-09 07:00:20
【问题描述】:

我正在与SQL server 2008 合作进行查询,该查询在距正在移动的中心点一定距离内查找用户 - 这意味着我经常访问数据库(新结果需要添加到现有集合中,而被删除),节省的每一毫秒都是有价值的。

我们正在使用以下查询的 ATM(使用 Id 是因为它们是索引 ATM 并且在尝试速度时很好):

declare @lat int = 500,
@lon int = 700

  SELECT @lat, @lon, id, journeyid,  ((ACOS(SIN(@lat * PI() / 180) * SIN([Id] * PI() / 180) + 
         COS(@lat * PI() / 180) * COS([Id] * PI() / 180) * COS((@lon - [JourneyId]) * 
         PI() / 180)) * 180 / PI()) * 60 * 1.1515) as dist
FROM [OnlineLegal_dev1].[dbo].[Customer] 
         group by [Id], [JourneyId], ((ACOS(SIN(@lat * PI() / 180) * SIN([Id] * PI() / 180) + 
         COS(@lat * PI() / 180) * COS([Id] * PI() / 180) * COS((@lon - [JourneyId]) * 
         PI() / 180)) * 180 / PI()) * 60 * 1.1515)
HAVING ((ACOS(SIN(@lat * PI() / 180) * SIN([Id] * PI() / 180) + 
         COS(@lat * PI() / 180) * COS([Id] * PI() / 180) * COS((@lon - [JourneyId]) * 
         PI() / 180)) * 180 / PI()) * 60 * 1.1515)<=10000

  ORDER BY ((ACOS(SIN(@lat * PI() / 180) * SIN([Id] * PI() / 180) + 
         COS(@lat * PI() / 180) * COS([Id] * PI() / 180) * COS((@lon - [JourneyId]) * 
         PI() / 180)) * 180 / PI()) * 60 * 1.1515) ASC

当前选择前 1k 记录的速度是 00:00:00.097

我怎样才能进一步优化它以提高速度?

【问题讨论】:

  • 你能解释一下你的公式吗(我猜是一些球面几何)?取身份证的窦看起来很奇怪。为什么不能用毕达哥拉斯简单计算距离?
  • @OlivierJacot-Descombes 使用 ID,因为这些列已编入索引,在实际应用程序中它将是纬度经度。它会弯曲以说明距离赤道更远的坐标的地球圆度。如果这不能很好地优化,我们可能使用的另一个选项是平方乘以国家/地区的常数,因为我们位于欧洲,这里的国家/地区很小。
  • 有什么理由不在这里使用地理数据类型并让数学在 CLR 中完成?
  • @BenThul TBH 我从未使用过它,但我仍然很难相信它会比纯 sql 解决方案更快,我错过了什么吗?
  • 是的,你是。 CLR 擅长(除其他外)像您正在做的计算繁重的事情。此外,您还可以利用 geography 类型的优势,而不是假设地球是一个完美的球体(就像您使用的 Haversine forumula 一样),而是一个扁球体。

标签: sql-server sql-server-2008 optimization query-optimization geospatial


【解决方案1】:
DECLARE @lat INT = 500,
        @lon INT = 700

DECLARE @lat_s FLOAT = SIN(@lat * PI() / 180),
        @lat_c FLOAT = COS(@lat * PI() / 180)

SELECT DISTINCT @lat, @lon, *
FROM (
    SELECT
        id,
        journeyid,
        ((ACOS(@lat_s * SIN([id] * PI() / 180) + @lat_c * COS([id] * PI() / 180) * COS((@lon - [JourneyId]) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS dist
    FROM dbo.Customer
) t
WHERE dist <= 10000
ORDER BY dist

【讨论】:

  • 非常聪明:) 奇怪的速度是一样的,我会添加更多的数据来显示。
  • 在索引和其他爵士乐对 47k 记录运行 100 次后,选择前 1000 名,其中接近度为 10000 - 我的约为 28 秒,你的约为 16 秒。
  • 可以找到类似的答案stackoverflow.com/q/44293480/5058871
【解决方案2】:

您可以存储预先计算的值

SIN([Id] * PI() / 180)

COS([Id] * PI() / 180)

在数据库中,例如每次插入或更新Id 时都会触发 INSERT / UPDATE。

CREATE TRIGGER dbo.tiuCustomer ON dbo.Customer
    FOR INSERT, UPDATE
AS BEGIN
   UPDATE dbo.Customer
   SET
       cos_id = COS(Inserted.Id * PI() / 180),
       sin_id = SIN(Inserted.Id * PI() / 180)
   FROM Inserted
   WHERE 
      dbo.Customer.CustomerID = Inserted.CustomerID -- Use the PK to link your table
                                                    -- with Inserted.
END

【讨论】:

    猜你喜欢
    • 2020-12-26
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    • 2016-07-02
    • 2011-01-18
    • 1970-01-01
    • 2015-08-25
    相关资源
    最近更新 更多