3

我在 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
4

5 回答 5

3

选项:

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

  2. 验证您的表中是否使用了索引。- 在表格上选择时,索引将使您的排序更快。

请将函数定义提供给我们,这样更容易帮助您。

最后,尝试直接在您的函数中移动您的订单并限制,而不是稍后再执行。您的函数可以返回直接排序并准备好的 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

于 2013-04-29T10:55:15.147 回答
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 行的函数结果,并检查差异是否仍然存在。

于 2013-04-29T11:10:25.583 回答
1

扩展你的功能:

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

因此 ordering byMyFunction(col1, col2, constant)等价于 ordering by2*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
于 2013-04-29T12:51:43.460 回答
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 使用临时表,但在只有数学时不使用。

于 2014-04-30T17:26:56.850 回答
0

尝试这个

  SELECT rowId, MyFunction(col1, col2, constant) AS funResult
  FROM myTable
  ORDER BY MyFunction(col1, col2, constant)  DESC
  LIMIT 10
于 2013-04-29T10:54:15.993 回答