当使用Order By with function计算两点之间的距离时,MySQL Query会变慢(long,lat)

时间:2013-04-29 10:49:02

标签: mysql database sorting

我在MySQL中有一个查询,它在表的每一行上运行一个存储函数,然后在返回前10行之前按函数的结果对行进行排序。

SELECT rowId, MyFunction(x, y, constX, constY) AS funResult
FROM myTable
ORDER BY funResult DESC
LIMIT 10

问题是在10,000行的表上运行需要几秒钟,这太慢了。函数的结果无法计算并存储为表中的另一行,因为它采用PHP给出的常量,并且每次运行查询时都不同。

函数本身的速度不是问题,因为删除ORDER BY funResult DESC LIMIT 10意味着查询在不到0.01秒的时间内运行。

问题必须是对行进行排序 - 考虑到只需要前10行这一事实,有没有办法可以更快地完成这一过程?

更新

使用的简化函数计算每一行与指定点之间的距离(其中LAT_B和LON_B是取决于查询的常量):

CREATE FUNCTION MyFunction(LAT_A float, LON_A float, LAT_B float, LON_B float)
RETURNS double
DETERMINISTIC
BEGIN

DECLARE tempCalc DOUBLE;
SET tempCalc = 3956 * 2 * ASIN(SQRT( POWER(SIN((LAT_A -abs( LAT_B)) * pi()/180 / 2),2)    
    + COS(LAT_A * pi()/180 ) * COS( abs(LAT_B) *  pi()/180)
    * POWER(SIN((LON_A - LON_B)
    * pi()/180 / 2), 2) ));

RETURN tempCalc;

END

5 个答案:

答案 0 :(得分:3)

选项:

  1. 在您的存储过程中合并排序 定义/逻辑。如果您的存储过程中的调用SQL选择执行排序并限制。 - 这意味着您不会在存储过程中生成10,000行,只是为了求助它们。此外,如果表具有索引,则SQL select中的原始排序可能会快得多。

  2. 验证您的表格中是否使用了索引。 - 索引会使您在桌面上选择时更快地执行排序。

  3. 请向我们提供功能定义,以便更方便地为您提供帮助。

    最后,尝试移动您的订单并直接限制您的功能,而不是稍后执行。您的函数可以直接返回10个结果并准备就绪。 如果你愿意,可以创建两个函数 - 一个返回完整的结果,另一个返回它们有限和排序。

    更新

    看到你的功能后,很明显你试图按计算值排序。按计算值排序非常慢,如下所述:

    我正在考虑如何根据col1或col2“预处理/订购”您的数据,以加快结果的最终排序。如果col1和col2是表的列,而funResult是一个可以绘制的数学函数,其中一个对函数返回值有更大的影响....

    最后,如果col1和col2是myTable的列,你不需要使用存储的函数但可以查询,但这不会产生很大的差异......你的主要问题是通过计算函数排序:

    SELECT rowId, ((col1-INPUT_CONST)*2)+(col2*3) AS funResult
    FROM myTable
    ORDER BY funResult DESC
    LIMIT 10
    

    更新2:

    在挖掘排序问题后计算距离我发现已经在下面的链接中非常有效地询问并解决了这个问题。关于按计算值排序,作为按计算值排序,它本身就很慢。请参阅以下两个链接以获取其他帮助:

    最后,最接近您答案的是:  的 https://stackoverflow.com/a/4180065/1688441

答案 1 :(得分:1)

我猜你的问题 是你执行函数的时间。如果您执行此查询:

SELECT rowId, MyFunction(col1, col2, constant) AS funResult
FROM myTable
LIMIT 10

数据库必须:

  • 计算10行的函数结果
  • 返回这10行

相反,如果您执行此查询:

   SELECT rowId, MyFunction(col1, col2, constant) AS funResult
   FROM myTable
   ORDER BY funResult DESC
   LIMIT 10

数据库必须

  • 计算表格中所有10000行的函数结果
  • 排序10000行
  • 返回前10行

因此,要真正了解您的函数是否是瓶颈,您应确保实际计算两个查询的所有10000行的函数结果,并检查差异是否仍然存在。

答案 2 :(得分:1)

扩展你的职能:

MyFunction(col1, col2, constant) = (col1 - constant) * 2.0 + col2 * 3.0
                                 = 2*col1 + 3*col2 - 2*constant

因此,按MyFunction(col1, col2, constant)排序相当于按2*col1 + 3*col2排序,无论提供的常数如何。因此,您可以将该计算的结果缓存在新的索引列中:

ALTER TABLE myTable
  ADD COLUMN tmpResult FLOAT,
  ADD INDEX (tmpResult);

CREATE TRIGGER ins BEFORE INSERT ON myTable FOR EACH ROW
  SET NEW.tmpResult := 2*NEW.col1 + 3*NEW.col2;

CREATE TRIGGER upd BEFORE UPDATE ON myTable FOR EACH ROW
  SET NEW.tmpResult := 2*NEW.col1 + 3*NEW.col2;

UPDATE myTable SET tmpResult = 2*col1 + 3*col2;

然后您的SELECT变为:

SELECT   rowId, tmpResult - 2*constant AS funResult
FROM     myTable
ORDER BY tmpResult DESC
LIMIT    10

答案 3 :(得分:1)

实际上,在mysql中实际上要快得多

select * from database order by 3956 * 2 * ASIN(SQRT( POWER(SIN((LAT_A -abs( LAT_B)) * pi()/180 / 2),2) + COS(LAT_A * pi()/180 ) * COS( abs(LAT_B) * pi()/180) * POWER(SIN((LON_A - LON_B) * pi()/180 / 2), 2) ));

比通过自定义功能订购。

它很难看但很快。

尝试对其进行解释。 出于某种原因,mysql在涉及函数时使用临时表,但在没有数学时则使用临时表。

答案 4 :(得分:0)

试试这个

  SELECT rowId, MyFunction(col1, col2, constant) AS funResult
  FROM myTable
  ORDER BY MyFunction(col1, col2, constant)  DESC
  LIMIT 10