为什么这个MySQL双元电话功能不能正常工作?

时间:2012-04-28 01:00:07

标签: mysql database algorithm metaphone

我刚学习Metaphone和Double Metaphone搜索算法,我有几个问题。根据Metaphone Wiki页面,我发现了几个带有实现的源,特别是MySQL实现。我想用我的测试数据库测试它,所以我首先导入了找到here

的metaphone.sql文件(包含双元电话功能)

现在,我有一个表格,国家/地区,其中包含“名称”列中所有国家/地区的列表,例如'阿富汗','阿尔巴尼亚','阿尔及利亚'等等。首先,我想在表格中创建一个新专栏来存储每个国家的双重Metaphone字符串。我运行了以下代码:

UPDATE country SET NameDM = dm(name)

一切正常。阿富汗的互联网字符串是'AFKNSTN',阿尔巴尼亚是'ALPN',阿尔及利亚是'ALKR; ALJR'等等。“真棒,”我想。

然而,当我试图查询表时,我没有得到任何结果。根据metaphone.sql的作者,我坚持以下SQL语句的语法:

SELECT Name FROM tblPeople WHERE dm(Name) = dm(@search)

所以,我将此代码更改为以下内容:

SELECT * FROM country WHERE dm(name) = dm(@search)

当然,我将“@search”更改为我正在寻找的任何搜索词,但在每次SQL查询后我得到0个结果。

有人能解释这个问题吗?我错过了重要的事情,还是我只是误解了Metaphone算法?

谢谢!

3 个答案:

答案 0 :(得分:3)

比较dm()输出时,我使用以下函数来进一步提高模糊度。直接检查dm('smith') != dm('schmitt')因大量名称而失败,包括我自己的常见拼写错误。

该函数创建一个0.0到1.0之间的匹配加权(我希望),它允许我对每个返回的行进行排名,并选择那些有益的,0.3对于捕获奇怪的发音非常好,0.5更常见。 / p>

即。 dmcompare(dm("boothroyd"), dm("boofreed")) = 0.3
dmcompare(dm("smith"), dm("scmitt")) = 0.5

请注意,这是双变音符字符串和原始字符串的比较,这是针对性能问题,我的数据库包含metaphone的列以及原始字符串。

    CREATE FUNCTION `dmcompare`(leftValue VARCHAR(55), rightValue VARCHAR(55)) 
        RETURNS DECIMAL(2,1) 
    NO SQL
    BEGIN
    ---------------------------------------------------------------------------------------
    -- Compare two (double) metaphone strings for potential similarlity, i.e.
    --    dm("smith") != dm("schmitt")  :: "SM0;XMT" != "XMT;SMT" 
    --  dmcompare( dm('smith'), dm('schmitt' ) returns 0,5
    -- @author: P.Boothroyd
    -- @version: 0.9, 08/01/2013
    -- The values here can still be played with
    -- (c) GNU P L - feel free to share and adapt, but please acknowledge the original code
    ---------------------------------------------------------------------------------------
        DECLARE leftPri, leftSec, rightPri, rightSec VARCHAR(55) DEFAULT '';
        DECLARE sepPos INT;
        DECLARE retValue DECIMAL(2,1);
        DECLARE partMatch BOOLEAN;

        -- Extract the metaphone tags
        SET sepPos = LOCATE(";", leftValue);
        IF sepPos = 0 THEN
            SET sepPos = LENGTH(leftValue) + 1;
        END IF;
        SET leftPri = LEFT(leftValue, sepPos - 1);
        SET leftSec = MID(leftValue, sepPos + 1, LENGTH( leftValue ) - sepPos);

        SET sepPos = LOCATE(";", rightValue);
        IF sepPos = 0 THEN
            SET sepPos = LENGTH(rightValue) + 1;
        END IF;
        SET rightPri = LEFT(rightValue, sepPos - 1);
        SET rightSec = MID(rightValue, sepPos + 1, LENGTH( rightValue ) - sepPos);

        -- Calculate likeness factor
        SET retValue = 0;
        SET partMatch = FALSE;
        -- Primaries equal 50% match
        IF leftPri = rightPri THEN
            SET retValue = retValue + 0.5;
            SET partMatch = TRUE;
        ELSE
            IF SOUNDEX(leftPri) = SOUNDEX(rightPri) THEN
                SET retValue = retValue + 0.3;
                SET partMatch = TRUE;
            END IF;
        END IF;
        -- Test alternate primary and secondaries, worth 30% match
        IF leftSec = rightPri THEN
            SET retValue = retValue + 0.3;
            SET partMatch = TRUE;
            IF SOUNDEX(leftSec) = SOUNDEX(rightPri) THEN
                SET retValue = retValue + 0.2;
                SET partMatch = TRUE;
            END IF;
        END IF;
        -- Test alternate primary and secondaries, worth 30% match
        IF leftPri = rightSec THEN
            SET retValue = retValue + 0.3;
            SET partMatch = TRUE;
            IF SOUNDEX(leftPri) = SOUNDEX(rightSec) THEN
                SET retValue = retValue + 0.2;
                SET partMatch = TRUE;
            END IF;
        END IF;
        -- Are secondary values the same or both NULL
        IF leftSec = rightSec THEN
            -- No secondaries ...
            IF leftSec = '' THEN
                -- If there is prior matching then no secondaries is 40%
                IF partMatch = TRUE THEN
                    SET retValue = retValue + 0.4;
                END IF;
            ELSE
                -- If the secondaries match then 50% match
                SET retValue = retValue + 0.5;
            END IF;
        ELSE
            IF SOUNDEX(leftSec) = SOUNDEX(rightSec) THEN
                IF leftSec = '' THEN
                    IF partMatch = TRUE THEN
                        SET retValue = retValue + 0.3;
                    END IF;
                END IF;
            END IF; 
        END IF;
        RETURN (retValue);
    END

请随意使用代码,但请提及此代码的来源 P.Boothroyd 以及任何用途 - 即更改值等。

干杯,保罗

答案 1 :(得分:2)

仔细查看排序/字符集/编码(可以将其定义为列级别)。排序规则定义了字符串的比较方式,但字符集可能意味着使用某种排序规则。也许你的文字字符串有不同的字符集,导致字符串比较失败。

即使这可能是揭示

select name, length(name), char_length(name), @search, length(@search), char_length(@search) from tbl

show variables like 'character%'

show create table tbl

答案 2 :(得分:2)

SELECT * FROM country WHERE NameDM = dm(@search)

最终可能是您想要的,因此您每次进行搜索时都不会为每个国家/地区计算DM。你看起来应该有什么用。你可以通过这样做来解决问题:

SELECT dm('Albania')

...应该让你ALPN。现在你得到什么......

SELECT * FROM country WHERE NameDM = 'ALPN'