2012-08-28 44 views
8

我有一個房地產列表的數據庫,並需要返回鄰居列表。現在我正在使用mysql DISTINCT,它返回所有不同的值。我萬阿英,蔣達清是,有很多社區的具有類似名稱:例如:模糊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 

我要尋找一個簡單的PHP或MySQL解決方案,將認識到「公園景觀」和「大明湖」已經存在,只返回「公園景觀」和「大湖」。

我最初的想法是如何按照長度獲取排序順序,以便短數值位於頂部,然後使用strstr循環。這聽起來像一個很大的任務,我想知道是否有一個函數在MySQL或PHP中,很容易做到這一點。

+0

你可以添加輸出需要你的問題,以更好地理解..? – heretolearn

+0

是「Sub X」唯一的字符串,它將在最後,還是該文本變量? –

+0

@sshekhar:「'只返回」Park View「和」Great Lake「。」 - 這是預期的輸出。 – Travesty3

回答

0

如果你總是有沒有「子#」部分的條目,你可以做這樣的事情:

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

排除Sub的唯一錯誤是,在「Park View Sub 1」是唯一的鄰居中,我希望它返回那個。我希望他們排除的唯一時間是如果已經有一個包含它的鄰域。 – user982853

0

您可以使用PHP的similar_text

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

要通過字符串長度排序得到一個簡單的解決方案。如果您預先對數據進行排序,以便首先使用較短的所需地址,那麼它應該可以很好地工作。此外,如果「不同」的地址是不是太相似,它會更好地工作(但你總是可以了閾值):

// 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()

另一種僞模糊方法是將地址按字母順序排序(通過MySQL或PHP)並循環遍歷它們;如果當前地址開始 - 已經找到了唯一地址的文本,請刪除它。這工作得同樣使用實際的模糊的方法,但它更直來了點:

// 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

它只返回公園景色bt它也應該返回綠湖,因爲這也是一個獨特的價值。 – heretolearn

0

下面這個例子查詢將讓你使用MySQL指定的結果集,但它並沒有真正做到「模糊匹配」,至少,這不是我會怎樣形容算法。 (這實現您所描述的算法。 - 通過值進行排序,然後檢查每個值,以查看是否引導部分「匹配」一個以前提取的值)

此發現的「完全匹配」龍頭部的鄰域值與以前檢索到的值相比較,但實際上並沒有任何有關匹配的「模糊性」。

當查詢遇到一個「不匹配」的值時,它表示該值爲「不匹配」。對於下一個檢索的值,它會檢查該值是否以先前的「不匹配」值開始;如果字符串的前導部分完全匹配,則丟棄該值。否則,該值被標記爲「不匹配」的值,並被保留。

此方法使用內聯視圖(或「派生表」,因爲MySQL引用它們)。最內層的內聯視圖(別名爲s)爲我們提供了一個用於鄰域的不同值的排序列表。 「訣竅」(如果你想這樣稱呼的話)在下一個內聯視圖中(別名爲「t」),我們利用MySQL用戶變量引用先前檢索的值。

爲了避免出現任何關於「特殊字符」的問題,我們對主要字符進行了平等比較。

這裏是整個查詢:

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:=「公園景觀」)和比較,爲的領導部分(直到'Park View'的長度)下一個值,確定它是否匹配。


這個查詢方法的一個好處是保證返回的值在後續查詢中的謂詞中匹配。假設您使用此查詢來獲取鄰居列表,並且用戶選擇了一個。這將返回一組將與每一行「匹配」的值。

後續查詢可以使用簡單謂詞(WHERE子句)中的任何返回值來返回匹配的行。例如,如果用戶選擇了值「大明湖」:

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

在我們使用相同或REGEXP謂詞相匹配的情況下,我們就需要使用相應的匹配中的謂語後續查詢:

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

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