3

我只是在学习 Metaphone 和 Double Metaphone 搜索算法,我有几个问题。根据 Metaphone Wiki 页面,我找到了几个实现的来源,特别是 MySQL 实现。我想用我的测试数据库对其进行测试,所以我首先导入了在这里找到的 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 算法?

谢谢!

4

3 回答 3

3

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

该函数创建了一个介于 0.0 和 1.0 之间的匹配权重(我希望如此),这允许我对返回的每个行进行排名,并选择那些有好处的行,0.3 对于捕获奇怪的发音来说是一个很好的值,0.5 更常见。

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

请注意,这是对双变音位字符串而不是原始字符串的比较,这是出于性能问题,我的数据库包含变位音位和原始字符串的列。

    创建函数`dmcompare`(leftValue VARCHAR(55), rightValue VARCHAR(55))
        返回小数(2,1)
    没有 SQL
    开始
    -------------------------------------------------- -------------------------------------
    -- 比较两个(双)变音位串的潜在相似性,即
    -- dm("smith") != dm("schmitt") :: "SM0;XMT" != "XMT;SMT"
    -- dmcompare(dm('smith'), dm('schmitt') 返回 0,5
    --@作者:P.Boothroyd
    --@版本:0.9,2013 年 8 月 1 日
    -- 这里的值还是可以玩的
    -- (c) GNU PL - 随意分享和改编,但请承认原始代码
    -------------------------------------------------- -------------------------------------
        DECLARE leftPri, leftSec, rightPri, rightSec VARCHAR(55) DEFAULT '';
        声明 sepPos INT;
        声明 retValue DECIMAL(2,1);
        声明 partMatch 布尔值;

        -- 提取变音位标签
        SET sepPos = LOCATE(";", leftValue);
        如果 sepPos = 0 那么
            SET sepPos = LENGTH(leftValue) + 1;
        万一;
        SET leftPri = LEFT(leftValue, sepPos - 1);
        SET leftSec = MID(leftValue, sepPos + 1, LENGTH(leftValue) - sepPos);

        SET sepPos = LOCATE(";", rightValue);
        如果 sepPos = 0 那么
            SET sepPos = LENGTH(rightValue) + 1;
        万一;
        SET rightPri = LEFT(rightValue, sepPos - 1);
        SET rightSec = MID(rightValue, sepPos + 1, LENGTH( rightValue ) - sepPos);

        -- 计算相似因子
        设置 retValue = 0;
        设置部分匹配 = 假;
        -- 初选等于 50% 匹配
        IF leftPri = rightPri THEN
            SET retValue = retValue + 0.5;
            设置部分匹配=真;
        别的
            IF SOUNDEX(leftPri) = SOUNDEX(rightPri) THEN
                SET retValue = retValue + 0.3;
                设置部分匹配=真;
            万一;
        万一;
        -- 测试备用主次,价值 30% 匹配
        IF leftSec = rightPri THEN
            SET retValue = retValue + 0.3;
            设置部分匹配=真;
            IF SOUNDEX(leftSec) = SOUNDEX(rightPri) THEN
                SET retValue = retValue + 0.2;
                设置部分匹配=真;
            万一;
        万一;
        -- 测试备用主次,价值 30% 匹配
        IF leftPri = rightSec THEN
            SET retValue = retValue + 0.3;
            设置部分匹配=真;
            IF SOUNDEX(leftPri) = SOUNDEX(rightSec) THEN
                SET retValue = retValue + 0.2;
                设置部分匹配=真;
            万一;
        万一;
        -- 次要值是否相同或都 NULL
        IF leftSec = rightSec THEN
            -- 没有二级...
            IF leftSec = '' 那么
                -- 如果有之前的匹配,那么没有二级是 40%
                如果 partMatch = TRUE 那么
                    SET retValue = retValue + 0.4;
                万一;
            别的
                -- 如果二级匹配,那么 50% 匹配
                SET retValue = retValue + 0.5;
            万一;
        别的
            IF SOUNDEX(leftSec) = SOUNDEX(rightSec) THEN
                IF leftSec = '' 那么
                    如果 partMatch = TRUE 那么
                        SET retValue = retValue + 0.3;
                    万一;
                万一;
            万一;
        万一;
        返回 (retValue);
    结尾

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

干杯,保罗

于 2013-08-01T19:20:09.873 回答
2

仔细查看排序规则/字符集/编码(可以定义到列级别)。排序规则定义了如何比较字符串,但字符集可以暗示使用某种排序规则。也许您的文字字符串具有不同的字符集,导致字符串比较失败。

即使这也可能会揭示

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

.

show variables like 'character%'

.

show create table tbl
于 2012-04-28T04:38:07.480 回答
2
SELECT * FROM country WHERE NameDM = dm(@search)

最终可能是您想要的,因此您每次进行搜索时都不会为每个国家/地区计算 DM。你所拥有的东西看起来应该可以工作。您可以通过以下方式排除故障:

SELECT dm('Albania')

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

SELECT * FROM country WHERE NameDM = 'ALPN'

?

于 2012-04-30T01:40:18.383 回答