如何在服务器中优化我的SQL?

时间:2009-10-02 03:03:34

标签: mysql optimization math explain

我想知道如何优化以下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来进行此查询。

3 个答案:

答案 0 :(得分:1)

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

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

我现在还没有时间了解完整的细节,但这里有一般的想法:
它是近似距参考ZipCode位置和其他位置的距离,用廉价(CPU计算)计算,并且只进行完整的数学计算(具有比原始查询),对于低于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&lt; 2500 - 2509是50 ^ 2

我猜这种类型的近似是可以接受的,因为考虑到通过道路的距离(可能是最终期望的距离)很少与'as-the-crow-相匹配的事实,会产生更大的误差。苍蝇';-)我可以计算精确的更精确的精度损失(但同样,现在没有时间......)

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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