我还能做些什么来优化这个MySQL查询吗?

时间:2010-04-25 19:09:24

标签: php stored-procedures mysql query-optimization

我有两个表,表A有700,000个条目,表B有600,000个条目。结构如下:

表A:

+-----------+---------------------+------+-----+---------+----------------+
| Field     | Type                | Null | Key | Default | Extra          |
+-----------+---------------------+------+-----+---------+----------------+
| id        | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment | 
| number    | bigint(20) unsigned | YES  |     | NULL    |                | 
+-----------+---------------------+------+-----+---------+----------------+

表B:

+-------------+---------------------+------+-----+---------+----------------+
| Field       | Type                | Null | Key | Default | Extra          |
+-------------+---------------------+------+-----+---------+----------------+
| id          | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment | 
| number_s    | bigint(20) unsigned | YES  | MUL | NULL    |                | 
| number_e    | bigint(20) unsigned | YES  | MUL | NULL    |                | 
| source      | varchar(50)         | YES  |     | NULL    |                |
+-------------+---------------------+------+-----+---------+----------------+

我试图使用以下代码查找表B中是否存在表A中的任何值:

$sql = "SELECT number from TableA";
$result = mysql_query($sql) or die(mysql_error());

while($row = mysql_fetch_assoc($result)) {
        $number = $row['number'];
        $sql = "SELECT source, count(source) FROM TableB WHERE number_s < $number AND number_e > $number GROUP BY source";
        $re = mysql_query($sql) or die(mysql_error);
        while($ro = mysql_fetch_array($re)) {
                echo $number."\t".$ro[0]."\t".$ro[1]."\n";
        }
}

我希望查询能够快速进行,但出于某种原因,它并不是很快。我对select的解释(具有特定值“number”)给出了以下内容:

mysql> explain SELECT source, count(source) FROM TableB WHERE number_s < 1812194440 AND number_e > 1812194440 GROUP BY source;
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+
| id | select_type | table      | type | possible_keys           | key  | key_len | ref  | rows   | Extra                                        |
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+
|  1 | SIMPLE      | TableB     | ALL  | number_s,number_e       | NULL | NULL    | NULL | 696325 | Using where; Using temporary; Using filesort | 
+----+-------------+------------+------+-------------------------+------+---------+------+--------+----------------------------------------------+
1 row in set (0.00 sec)

我可以从中挤出任何优化吗?

我尝试为同一个任务编写一个存储过程,但它似乎一开始似乎没有工作......它没有给出任何语法错误...我尝试运行它一天,它是还在跑步,感觉很奇怪。

CREATE PROCEDURE Filter() 
Begin 
  DECLARE number BIGINT UNSIGNED; 
  DECLARE x INT; 
  DECLARE done INT DEFAULT 0; 
  DECLARE cur1 CURSOR FOR SELECT number FROM TableA; 
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 
  CREATE TEMPORARY TABLE IF NOT EXISTS Flags(number bigint unsigned, count int(11)); 
  OPEN cur1; 
  hist_loop: LOOP 
    FETCH cur1 INTO number; 
    SELECT count(*) from TableB WHERE number_s < number AND number_e > number INTO x; 
    IF done = 1 THEN 
      LEAVE hist_loop; 
    END IF; 
    IF x IS NOT NULL AND x>0 THEN 
      INSERT INTO Flags(number, count) VALUES(number, x); 
    END IF; 
  END LOOP hist_loop; 
  CLOSE cur1;
END

3 个答案:

答案 0 :(得分:4)

您正在尝试查找包含点的间隔。对于B-tree索引(大多数数据库中的默认索引类型),这不是那么快,但R-tree索引适用于此类查询。 MySQL不允许您直接更改索引的类型,但您可以通过使用GEOMETRY列类型强制MySQL使用R-Tree。

Quassnoihis article on nested sets in MySQL中介绍了这一点。虽然它不完全相同,但它非常相似。引文来自文章:

  

还有一类任务   需要搜索所有范围   包含已知值:

* Searching for an IP address in the IP range ban list
* Searching for a given date within a date range
     

和其他几个人。这些任务可以   通过使用R-Tree功能进行了改进   的MySQL

答案 1 :(得分:2)

我认为您在number_enumber_s列上有单独的索引,可能是使用单独的ADD INDEX(number_e)ADD INDEX(number_s)列创建的。

如果你添加一个包含这两列的索引,你可能会获得更好的性能,因为它们都在你的查询中使用,而且MySQL显然没有选择使用任何一个单列索引,判断一个整个表扫描会更快(如果您的查询跨越大范围的值,则并不罕见)。

ALTER TABLE tblB ADD INDEX(number_s,number_e);

之后您不需要单独的number_s索引,因为MySQL可以使用您刚刚针对number_s的查询创建的索引,因此您可以删除该索引。

答案 2 :(得分:1)

首先,我假设所需的输出是对输入所在的所有“源”进行分组 number_e和number_s及其计数。

我对语法很苛刻,但您可能会考虑在那里使用'BETWEEN'子句而不是使用less-than / greater-than运算符进行显式比较

编辑:Zombat说的也适用;索引也会有所帮助。