2012-08-28 7 views
8

私は不動産リストのデータベースを持っており、近隣のリストを返す必要があります。今私は、別の値のすべてを返すmysqlのDISTINCTを使用しています。例:私は「パークビュー」と「大湖」は既に存在していることを認識してしまう簡単なPHPやMySQLのソリューションを探していますファジーDISTINCT値

Park View Sub 1 
Park View 
Park View Sub 2 
Park View Sub 3 
Great Lake Sub 1 
Great Lake Sub 2 
Great Lake 
Great Lake Sub 3 

マイprobelmは、似た名前を持つ地域がたくさんあるということです「パークビュー」と「グレートレイク」だけを返します。

私の最初の考えは、短い値が一番上にあり、strstrを使ってループするように、ソート順を長さで取得する方法です。このような大きな課題のようなサウンドは、mysqlやphpの中に簡単にこれを行う関数があるかどうか疑問に思っています。

+0

あなたの質問に必要な出力を追加して理解を深めることはできますか? – heretolearn

+0

"Sub X"は最後にある唯一の文字列か、そのテキスト変数ですか? –

+0

@sshekhar: "' 'Park View ''と 'Great Lake' 'のみを返します。" - これは予想される出力です。 – Travesty3

答えて

0

あなたは常に「サブ#」の部分なしでエントリを持っている場合、あなたはこのような何か行うことができます:文字列の長さでソートするには

SELECT DISTINCT neighborhood FROM table WHERE neighborhood NOT LIKE '% Sub %'; 

を:

SELECT DISTINCT neighborhood FROM table ORDER BY LENGTH(neighborhood); 
+3

Subを除外することに間違いがあるのは、 "Park View Sub 1"が唯一のイベントである場合、それを返すことです。私がそれらを除外したい唯一の時間は、すでにそれが含まれている近隣がある場合です。 – user982853

0

あなたがPHPのsimilar_textを使用することができます実装された簡単なソリューションを得ることができます。短く、必要なアドレスが最初になるようにデータを事前に並べ替えると、うまくいくはずです。また、「異なる」アドレスがあまりにも似ていない場合、それは良い仕事します(しかし、あなたは常にしきい値UP缶):他の選択肢については

// if an address is 70% (or more) similar to another, it is not unique 
$threshold = 70; 

// list of addresses (and sorting them); this is done through the DB in your code 
$addresses = array('Park View Sub 1', 'Park View', 'Park View Sub 2', 'Park View Sub 3', 'Great Lake Sub 1', 'Great Lake Sub 2', 'Great Lake', 'Great Lake Sub 3'); 
sort($addresses); 

$unique = array(); 
foreach ($addresses as $address) { 
    $isUnique = true; 
    foreach ($unique as $u) { 
     // get the similarity between the current address and each unique address 
     similar_text($address, $u, $percent); 
     if ($percent > $threshold) { 
      // not unique; drop it 
      $isUnique = false; 
      break; 
     } 
    } 
    if ($isUnique) $unique[] = $address; 
} 

、あなたも同様に、PHPのlevenshteinsoundexに見ることができますMySQLのSOUNDEX()と同じです。

もう1つの疑似ファジーメソッドは、アドレスをアルファベット順に(MySQLまたはPHPを介して)ソートし、それらを1つずつループすることです。現在のアドレスが始まっていれば、すでに見つかった一意のアドレスのテキストをドロップします。これは、実際のファジー・メソッドを使用してと全く同様に動作しますが、それはよりストレート・トゥ・ポイントです:

// list of addresses (and sorting them); this is done through the DB in your code 
$addresses = array('Park View Sub 1', 'Park View', 'Park View Sub 2', 'Park View Sub 3', 'Great Lake Sub 1', 'Great Lake Sub 2', 'Great Lake', 'Great Lake Sub 3'); 
sort($addresses); 

$unique = array(); 
foreach ($addresses as $address) { 
    $isUnique = true; 
    foreach ($unique as $u) { 
     if (substr($address, 0, strlen($u)) == $u) { 
      $isUnique = false; 
      break; 
     } 
    } 
    if ($isUnique) $unique[] = $address; 
} 

彼らはソートされている場合は、この方法が唯一のPark Viewがする必要があるだろう短いアドレスとして、動作します前に見つけたPark View Sub 1。あなたのアドレスがでもと類似していて、上記のsimilar_textメソッドがあまりにも多くを落とすなら、もっと厳しいのでこの後者の機能を試すことができます。

2

お試しいただけることがいくつかあります。恐らくあなたは正確なマッチと近いマッチの両方を探しているでしょう。

最初に完全一致を探します。 その後、REVERSED名でLIKEの一致を探します。 次に、最小の余分な文字で一致するものを探します。

これをすべて実行するクエリがあります。これを効率的にするには、逆の場所名をインデックス付きの列に格納する必要があります。

select name 
    from (
    select name, 0 ordinal 
    from place 
    where name = 'Park View' 
    union 
    select name, 1 ordinal 
    from place 
    where Reverse(Name) like concat(Reverse('Park View'),'%') 
    union 
    select name, 2+length(name) 
    from place 
    where name like concat('Park View','%') 
) a 
order by ordinal 
    limit 1 

このUNIONクエリがベストマッチを把握するordinalをどのように使用するかに注意してください。

ここでそれをチェックアウト:少なくとも、それは私が説明するだろうかではありません、以下http://sqlfiddle.com/#!2/76a97/9/0

+0

これはパークビューだけを返すので、緑の湖も戻ってくるはずです。 – heretolearn

0

例のクエリは、あなたのMySQLを使用して、指定された結果セットを取得しますが、それは本当に「ファジーマッチング」を行いません。アルゴリズム。 (これはあなたが記述するアルゴリズム実装 - 値によってソートし、次に先頭部分「マッチ」以前に取得した値かどうかを確認するために各値を確認する)

これは、リーディング部分の「完全一致」を見つけます以前に検索された行からの値に対する近傍値のうち、実際に一致についての「あいまいさ」はありません。

"不一致"の値をクエリが検出すると、その値は "不一致"とマークされます。取り出された次の値に対して、その値が前回の "不一致"値で始まるかどうかがチェックされます。文字列の先頭部分が完全一致の場合、値は破棄されます。それ以外の場合、値は「不一致」値としてマークされ、保持されます。

このアプローチでは、インラインビュー(またはMySQLが参照する「派生テーブル」)を使用します。最も内側のインラインビュー(sのエイリアス)は、近傍の異なる値の並べ替えられたリストを取得します。 「トリック」(それを呼びたい場合)は、以前に取得した値を参照するためにMySQLユーザ変数を使用する次のインラインビュー(別名「t」)にあります。

「特殊文字」の問題を避けるために、先頭の文字を等価比較します。

は、ここで全体のクエリです:

SELECT t.neighborhood 
    FROM (
     SELECT IF(IFNULL(LEFT(s.neighborhood,CHAR_LENGTH(@match)) <> @match,1),@match := s.neighborhood,NULL) AS neighborhood 
      FROM (SELECT RTRIM(neighborhood) AS neighborhood 
        FROM mytable 
        JOIN (SELECT @match := NULL) r 
        GROUP BY neighborhood 
        ORDER BY neighborhood 
       ) s 
     ) t 
WHERE t.neighborhood IS NOT NULL 

それは@match変数の初期化、および以前の値に現在の値の比較を行い、表現を除いて、すべて本当に非常に簡単です。

我々は値に特殊文字によって導入されたコーナーケースと心配していない場合、我々は比較を行うために単純なLIKEまたはREGEXPを使用することができます。

s.neighborhood NOT LIKE CONCAT(@match,'%') 

s.neighborhood NOT REGEXP CONCAT('^',@match) 

LIKE演算子は、アンダースコアの対象となりますパーセント文字の場合、REGEXPは正規表現で使用される特殊文字の影響を受けます。

LEFT(s.neighborhood,CHAR_LENGTH(@match)) <> @match 

何それは前の値を取ってやっている(例えば@match::=「パークビュー」)とすることを比較し、それらの問題を回避するには、クエリは、上記もう少し探して扱いにくいです比較を使用しています次の値のリーディング部分(「パークビュー」の長さまで)が一致するかどうかを判断します。このクエリでのアプローチの


1つの利点は、返される値は、後続のクエリ内の述語にTP「一致」を保証されているということです。このクエリを使用して近隣のリストを取得しており、ユーザーが選択したとします。これは、各行に「一致」する値のセットを返すことになります。

次の問合せでは、単純述語(WHERE句)の戻り値のいずれかを使用して一致する行を戻すことができます。ユーザーが値「大湖」を選択している場合たとえば、:我々は一致するLIKEまたはREGEXP述語を使用する場合には

SELECT t.* 
    FROM mytable t 
WHERE LEFT(t.neighborhood,CHAR_LENGTH('Great Lake') = 'Great Lake' 

を、我々は述語に対応するマッチを使用したいと思いますその後のクエリ:

SELECT t.* 
    FROM mytable t 
WHERE t.neighborhood LIKE CONCAT('Great Lake','%') 

SELECT t.* 
    FROM mytable t 
WHERE t.neighborhood REGEXP CONCAT('^','Great Lake')