web-dev-qa-db-ja.com

MySQLでBTREEインデックスを可能な限り歩く

BTREEインデックスを作成する単語の列があるとします。

CREATE TABLE myTable (
  words VARCHAR(25),
  INDEX USING BTREE (words)
);

LOAD DATA LOCAL INFILE '/usr/share/dict/words' INTO TABLE myTable (words);

そして今、いくつかの検索クエリと最長の共通プレフィックスを共有するレコードを見つけたいと思います。 'foobar'私は思った 次のことをする:

SELECT DISTINCT words
FROM   myTable
WHERE  words LIKE CASE
  WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'f%') THEN '%'
  WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'fo%') THEN 'f%'
  WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'foo%') THEN 'fo%'
  WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'foob%') THEN 'foo%'
  WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'fooba%') THEN 'foob%'
  WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'foobar%') THEN 'fooba%'
  ELSE 'foobar%'
END

これは問題ありません。非常に読みやすく、パフォーマンスが優れています。また、アプリケーションコードで簡単に生成できます。

ただし、この検索はさらに簡単に解決できるはずです。ブランチが存在しなくなるまで検索語に従ってインデックスツリーをたどり、現在のノードからブランチしたすべての結果を返します。

インデックスを複数回ではなく1回だけパスをたどることは、おそらく不必要なマイクロ最適化ですが、それは可能であるかのように感じます:それは...ですか?

5
eggyal

本当にb-treeインデックスだけを調べたい場合は、innodb_Rubyプロジェクトを使用すると役立ちます http://blog.jcole.us/2013/01/14/efficiently-traversing-innodb-btrees-with-the -page-directory /


私はあなたの論理があなたの質問で逆になっていると思います。最短の単語から始めます。

私がそれをどのように扱うかはこれです

DROP PROCEDURE IF EXISTS `find_longest_prefix`;

DELIMITER $$

CREATE PROCEDURE `find_longest_prefix`(IN `Word` varchar(255), OUT `Word_prefix` varchar(255))
BEGIN
    SET max_sp_recursion_depth = 255;
    SET @nextWord = LEFT(`Word`, LENGTH(`Word`)-1);

    SELECT COUNT(DISTINCT `words`) FROM `myTABLE` WHERE `words` LIKE CONCAT(`Word`, '%') INTO @Word_count;

    IF (@Word_count > 0)
    THEN
        SET `Word_prefix` = `Word`;
    ELSE
        IF (LENGTH(@nextWord) > 0)
        THEN
            Call `find_longest_prefix`(@nextWord, `Word_prefix`);
        ELSE
            SET `Word_prefix` = '';
        END IF;
    END IF;
END$$

DELIMITER ;

これは、btreeでミスを見つけるのが速いという事実を利用しているので、ヒットするまで再帰的に呼び出しをループします。

結果がありません

mysql> CALL find_longest_prefix(';autobon', @prefix);
Query OK, 1 row affected (0.01 sec)

mysql> SELECT @prefix;
+---------+
| @prefix |
+---------+
|         |
+---------+
1 row in set (0.00 sec)

いくつかの結果

mysql> CALL find_longest_prefix('autobon', @prefix);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT @prefix;
+---------+
| @prefix |
+---------+
| auto    |
+---------+
1 row in set (0.00 sec)

ご覧のとおり、データは正しいです。

mysql> SELECT * FROM myTable WHERE words LIKE 'auto%' OR words LIKE ';auto%';
+----+-----------------+
| id | words           |
+----+-----------------+
| 19 | AUTOCOMMIT      |
| 20 | AUTOEXTEND_SIZE |
+----+-----------------+
2 rows in set (0.00 sec)

このメソッドは、実際にすべてのステップを最長の前にチェックして、より多くの返されるデータを取得するよりもはるかに高速である必要があります。

正しいプレフィックスが見つかったら、ストアドプロシージャに行を選択させ、必要に応じてそれらを返すのは簡単です。

1
R. S.