web-dev-qa-db-ja.com

レーベンシュタイン:MySQL + PHP

$Word = strtolower($_GET['term']); 

$lev = 0;

$q = mysql_query("SELECT `term` FROM `words`"); 
while($r = mysql_fetch_assoc($q)) 
{ 
    $r['term'] = strtolower($r['term']); 

    $lev = levenshtein($Word, $r['term']);

    if($lev >= 0 && $lev < 5)
    {
        $Word = $r['term'];
    }
}

これらすべてを1つのクエリに移動するにはどうすればよいですか?すべての用語をクエリしてPHPでフィルタリングする必要はありません。

31
user317005

MySQLのレベンシュタイン関数 が必要で、

$Word = mysql_real_escape_string($Word);
mysql_qery("SELECT `term` FROM `words` WHERE levenshtein('$Word', `term`) BETWEEN 0 AND 4");
59
rik

MySQLでレーベンシュタイン関数を実装するには2つの方法があります。 1つ目は、STORED TRANSACTIONとほぼ同じように動作するSTORED FUNCTIONを作成することですが、入力と出力が異なります。これは小さなデータセットでは問題ありませんが、数千行に近づくと少し遅くなります。

CREATE FUNCTION levenshtein( s1 VARCHAR(255), s2 VARCHAR(255) )
RETURNS INT
DETERMINISTIC

BEGIN
DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
DECLARE s1_char CHAR;
-- max strlen=255
DECLARE cv0, cv1 VARBINARY(256);
SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0;
IF s1 = s2 THEN
  RETURN 0;
ELSEIF s1_len = 0 THEN
  RETURN s2_len;
ELSEIF s2_len = 0 THEN
  RETURN s1_len;
ELSE
  WHILE j <= s2_len DO
    SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1;
  END WHILE;
  WHILE i <= s1_len DO
    SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1;
    WHILE j <= s2_len DO
    SET c = c + 1;
    IF s1_char = SUBSTRING(s2, j, 1) THEN
      SET cost = 0; ELSE SET cost = 1;
    END IF;
    SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost;
    IF c > c_temp THEN SET c = c_temp; END IF;
      SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1;
      IF c > c_temp THEN
        SET c = c_temp;
      END IF;
      SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1;
    END WHILE;
    SET cv1 = cv0, i = i + 1;
  END WHILE;
END IF;

RETURN c;

END//

上記のコードを.sqlファイルに保存し、次のようにデータベースにインポートします。

source /tmp/mysql_udf.sql

2番目の方法は、C/C++でユーザー定義関数を実装し、それを共有ライブラリ(* .soファイル)としてMySQLにリンクすることです。このメソッドはまた、STORED FUNCTIONを使用してライブラリを呼び出します。つまり、このメソッドまたは最初のメソッドの実際のクエリは同一である可能性があります(両方の関数への入力が同じである場合)。この方法の詳細については、こちらをご覧ください。 http://samjlevy.com/mysql-levenshtein-and-damerau-levenshtein-udfs/

これらの方法のいずれかを使用すると、クエリは次のようになります。

SELECT term FROM words WHERE levenshtein(term, 'term') < 5;

また、「しきい値」の値は、元の単語の長さに対して変更する必要があることも覚えておいてください。パーセント値で考えるとよいでしょう。つまり、単語の半分= 50%、「用語」の半分= 2です。

11
Gordon Freeman

巨大なデータベースがある場合は、最初にSOUNDEXを使用して単語をフィルタリングできます。

$Word = strtolower(mysql_real_escape_string($_GET['term']));

$rs = mysql_query("SELECT LOWER(`term`) FROM `words` WHERE SOUNDEX(term) = SOUNDEX(" . $Word . ")");

while ($row = mysql_fetch_assoc($rs)) { 

    $lev = levenshtein($Word, $row['term']);

    ....

}

Cの拡張機能やプロシージャで遊ぶ時間がある場合は、パフォーマンスを向上させることができますが、実際のレーベンシュタインを適用する前にmysqlでレコードをフィルタリングすると、ほとんど労力をかけずに処理が速くなります。

8
carlosvini

非常に大きなデータセットを処理している場合、Levenshteinの操作を処理し、PHPでソートする方が、MySQLの場合よりもはるかに効率的です。たとえば、約1000レコードのクエリ:

MySQL(〜0.0050秒)-> PHPレーベンシュタイン(〜1.300秒)

vs.

MySQL Levenshtein(> = 5.000s)-> PHP(〜0.250s)

検索エンジンを最適化する他の多くのオプションもありますが、レーベンシュタインを使用する場合は、処理するデータと必要なレイテンシに注意してください。

6
John Rausch

クエリにlevenshtein(link: http://www.artfulsoftware.com/infotree/queries.php#552 )の呼び出しを含めることをお勧めします。

Mysql_query($ q)は非推奨であり、将来のphpバージョンで削除される可能性があるため、mysqli_query($ q)を使用する必要があります!

$Word = mysql_real_escape_string($Word);
$query = "SELECT `term` FROM `words` WHERE levenshtein('$Word', `term`)   BETWEEN 0 AND 4";
mysqli_qery($query);
1
Coder55

このコードを少しすっきりと見せることはできますが、@ profitphpは正しいです。MySQLでは、レベンシュタインライブラリなしではそれを行うことはできません。

 $ Word = strtolower($ _ GET ['term']); 
 
 $ q = mysql_uqery( "SELECT LOWER(` term`)FROM `words`"); 
 
 while($ r = mysql_fetch_assoc($ q)){
 
 $ lev = levenshtein($ Word、$ r ['term']); 
 
 .... 
 
} 
1
mvbl fst

Oracleでは、呼び出すことができる関数内のPL/SQLにアルゴリズムを実装することでこれを行っています。

0
Randy