0

我想知道如何优化以下 SQL 以让我的服务器加载更快并降低使用率?

我需要计算美国邮政编码的半径距离以获得结果,例如距特定邮政编码 50 英里(使用纬度和经度计算)并从我的数据库中获取多少其他数据(例如其他邮政编码) .

一旦我得到结果(例如,在距特定邮政编码 50 英里的范围内得到 350 行不同的邮政编码),我需要将它们传递到另一个查询中以计算总行数并以简单的方式显示它,并显示一个结果供我阅读。这是我的查询示例:

SELECT count(*)
FROM
( SELECT b.ID, ROUND((acos(sin(3.142/180*32.91336) * sin(3.142/180*z.latitude) + cos(3.142/180*32.91336) * cos(3.142/180*z.latitude) * cos((3.142/180*z.longitude) - (3.142/180*-85.93836))) * 3959),2) AS distance
  FROM zipcode2business.accountants b LEFT JOIN zipcodeworld.storelocator_us z ON b.ZIPCODE = z.ZIP_CODE
  WHERE z.latitude != 32.91336 AND z.longitude != -85.93836
  AND b.STATE='AL'
  HAVING distance between 0 AND 50) 
as total;

希望我没有做错,它显示正确的结果(350 行),但我需要一种优化的方式来运行它,因为这个 SQL 给了我很高的 CPU 使用率来加载。当我对此查询执行 EXPLAIN 时,它显示以下内容:

+----+-------------+-------+--------+------------------+---------+---------+----------------------------+------+------------------------------+
| id | select_type | table | type   | possible_keys    | key     | key_len | ref                        | rows | Extra                        |
+----+-------------+-------+--------+------------------+---------+---------+----------------------------+------+------------------------------+
| 1 | PRIMARY      | NULL  | NULL   | NULL             | NULL    | NULL    |        NULL                | NULL | Select tables optimized away |
| 2 | DERIVED      | b     | ref    | ZIPCODE,STATE    | STATE   | 4       |                            | 3900 | Using where                  |
| 2 | DERIVED      | z     | eq_ref | PRIMARY,LAT_LONG | PRIMARY | 9       | zipcode2business.b.ZIPCODE | 1    | Using where                  |
+----+-------------+-------+--------+------------------+---------+---------+----------------------------+------+------------------------------+
3 rows in set (0.20 sec)

现在,从上面的解释来看,EXTRA 中的“选择优化掉的表”是一件好事吗?请向我展示一个最完美的优化 SQL 来执行此查询。

4

3 回答 3

1

SQL 本身似乎很好,大部分 CPU 时间必须花在做数学上......有两种优化途径

  • 简化公式
  • 基于更简单的计算提前过滤掉行(“修剪”)

我目前没有时间了解完整的细节,但这里的总体思路是:
它是通过廉价(CPU 方面)计算来近似与参考 ZipCode 位置和其他位置的距离,并且只进行完整的数学运算(使用比原始查询中的公式更好的公式),对于 50 英里以下的位置(+ 一个小的额外,以解释可能的低估)。

估计距离和修剪
我们计算一次,以英里为单位的距离,对应于一个纬度和一个经度,从参考邮政编码位置;将这些称为 MpDLat 和 MpDLong。可能我们从参考位置计算与我们的目标半径相对应的度数分数值;将这些称为 Dp50Lat 和 Dp50Long。然后使用相对于参考位置的纬度之间和经度之间的[绝对值]差异,并过滤掉一个方向(纬度或经度)上的距离超过我们限制的位置。即类似于以下内容

WHERE .... (some other condidtions....) 
   AND (abs(z.latitude - 32.91336) * MpDLat) < 50 
   AND (abs(z.longitude + 85.93836) * MpDLong) < 50 
--or, if we got by the Dp50 values
WHERE .... (some other condidtions....) 
   AND (abs(z.latitude - 32.91336)  < Dp50Lat
   AND (abs(z.longitude + 85.93836) < Dp50Long 

计算距离(对于那些不容易过滤的位置)
根据所需的精度水平,坚持使用 MpD 因素可能是可以接受的(我猜测误差小于一英里左右,距离大约为 50英里,在美国大陆内)。然后距离将被计算为: Sqrt((z.latitude - 32.91336)^2 + (z.longitude + 85.93836)^2 或者,如果我们只对过滤这些而不需要距离本身感兴趣,我们可以工作离开正方形,即... WHERE (z.latitude - 32.91336)^2 + (z.longitude + 85.93836)^2 < 2500 -- 2509 是 50^2

我猜这种近似是可以接受的,因为考虑到道路距离(这可能是最终想要的距离)很少与“乌鸦苍蝇”的距离相匹配,因此会产生更大的错误; -) 我可以计算出精确的更坏情况下的精度损失(但同样,现在没时间了……)

如果需要精确的距离,我们使用比原始公式稍微好一点的公式,这个公式似乎直接来自余弦的球面定律。我们或许可以做得更好。

上述变化 上面
讨论的想法可以以各种方式实现,例如使用临时 SQL 表或查询的各种构造等。

于 2009-10-02T05:03:50.753 回答
0

您是否需要在 sql server 上进行所有这些计算?我通常尝试仅将 SQL 用于数据的基本 CRUD,然后所有其他计算都在 SQL 之外完成。您可能想尝试检索您计算所依据的数据,然后使用检索数据的任何内容进行实际计算。

于 2009-10-02T03:37:17.040 回答
0

您可以将距离计算选择到临时表中并从 SQL 中删除 HAVING,然后执行第二次 SELECT WHERE dist <= 50

这有助于节省内存空间,并可能为基表中的大量记录换出临时磁盘段

于 2009-10-08T11:54:16.760 回答