Что еще я могу сделать, чтобы оптимизировать этот запрос MySQL? - PullRequest
2 голосов
/ 25 апреля 2010

У меня есть две таблицы: таблица 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    |                |
+-------------+---------------------+------+-----+---------+----------------+

Я пытаюсь выяснить, присутствуют ли какие-либо значения в таблице A в таблице B, используя следующий код:

$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";
        }
}

Я надеялся, что запрос будет выполнен быстро, но затем по какой-то причине он не очень быстрый. Мое объяснение выбора (с конкретным значением «число») дает мне следующее:

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 ]

4 голосов
/ 25 апреля 2010

Вы пытаетесь найти интервалы, содержащие точку. Это не так быстро с индексом B-tree (тип индекса по умолчанию в большинстве баз данных), однако индекс R-tree будет хорошо работать для такого рода запросов. MySQL не позволяет вам напрямую изменять тип индекса, но вы можете заставить MySQL использовать R-Tree, используя тип столбца GEOMETRY.

Quassnoi описывает это в своей статье о вложенных множествах в MySQL . Хотя это не совсем то же самое, это очень похоже. Цитата из статьи:

Существует также определенный класс задач которые требуют поиска по всем диапазонам содержащий известное значение:

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

и несколько других. Эти задачи могут быть улучшено с помощью возможностей R-Tree MySQL

2 голосов
/ 25 апреля 2010

Мне кажется, у вас есть отдельные индексы для столбцов number_e и number_s, вероятно, созданные с отдельными столбцами ADD INDEX(number_e) и ADD INDEX(number_s).

Вероятно, вы получите гораздо лучшую производительность, если добавите индекс, который охватывает оба этих столбца, так как они оба используются в вашем запросе, и MySQL явно не выбирает использовать один из индексов с одним столбцом, судя по сканирование всей таблицы будет быстрее (не редкость, если ваш запрос охватывает большой диапазон значений).

ALTER TABLE tblB ADD INDEX(number_s,number_e);

После этого вам не понадобится отдельный индекс number_s, поскольку MySQL может использовать только что созданный вами для запросов только к number_s, так что вы также можете его удалить.

1 голос
/ 25 апреля 2010

Во-первых, я предполагаю, что желаемым выходом является группировка всех «источников», где вход находится между number_e и number_s и их количество.

Я немного разбираюсь в синтаксисе, но вы можете рассмотреть возможность использования предложения BETWEEN вместо явного сравнения с использованием операторов меньше / больше

Редактировать: То, что говорит Зомбат, также применимо; индексы тоже помогут.

...