【问题标题】:SQL Server Weighted Full Text SearchSQL Server 加权全文搜索
【发布时间】:2008-11-21 22:27:41
【问题描述】:

目前我有一个表,我搜索 4 个字段,FirstName、LastName、MiddleName 和 AKA。我目前有一个 CONTAINSTABLE 搜索行并且它有效。不太好,但它有效。现在我想让名字的权重更高,中间名的权重更低。

我找到了命令 ISABOUT 但如果我必须按单词而不是列来执行它似乎毫无价值(希望我理解错了)。如果按单词的话,这不是一个选项,因为我不知道用户会输入多少个单词。

我发现线程 here 讨论了相同的解决方案,但是我无法让公认的解决方案起作用。也许我做错了什么,但无论如何我都无法让它发挥作用,它的逻辑似乎真的……很奇怪。必须有一个更简单的方法。

【问题讨论】:

    标签: sql-server full-text-search


    【解决方案1】:

    操纵排名的关键是使用联合。对于每一列,您使用单独的选择语句。在该语句中,添加一个标识符,该标识符显示随后从哪一列中提取了每一行。将结果插入表变量中,然后您可以通过对标识符进行排序或将排名乘以基于标识符的某个值来操纵排名。

    关键是给出修改排名的表象,而不是实际改变sql server的排名。

    使用表变量的示例:

    DECLARE @Results TABLE (PersonId Int, Rank Int, Source Int)
    

    对于表 People with Columns PersonId Int PK Identity, FirstName VarChar(100), MiddleName VarChar(100), LastName VarChar(100), AlsoKnown VarChar(100),每列都添加到全文目录中,您可以使用查询:

    INSERT INTO @Results (PersonId, Rank, Source)
    
    SELECT PersonId, Rank, 1
    FROM ContainsTable(People, FirstName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId
    
    UNION
    SELECT PersonId, Rank, 2
    FROM ContainsTable(People, MiddleName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId
    
    UNION
    SELECT PersonId, Rank, 3
    FROM ContainsTable(People, LastName, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId
    
    UNION
    SELECT PersonId, Rank, 4
    FROM ContainsTable(People, AlsoKnown, @SearchValue) CT INNER JOIN People P ON CT.Key = P.PersonId
    
    /*
    Now that the results from above are in the @Results table, you can manipulate the
    rankings in one of several ways, the simplest is to pull the results ordered first by Source then by Rank.  Of course you would probably join to the People table to pull the name fields.
    */
    
    SELECT PersonId
    FROM @Results
    ORDER BY Source, Rank DESC
    
    /*
    A more complex manipulation would use a statement to multiply the ranking by a value above 1 (to increase rank) or less than 1 (to lower rank), then return results based on the new rank.  This provides more fine tuning, since I could make first name 10% higher and middle name 15% lower and leave last name and also known the original value.
    */
    
    SELECT PersonId, CASE Source WHEN 1 THEN Rank * 1.1 WHEN 2 THEN Rank * .9 ELSE Rank END AS NewRank FROM @Results
    ORDER BY NewRank DESC
    

    一个缺点是你会注意到我没有使用UNION ALL,所以如果一个词出现在多个列中,排名将不会反映这一点。如果这是一个问题,您可以使用UNION ALL,然后通过将所有或部分重复记录的排名添加到具有相同人员 ID 的另一条记录的排名来删除重复的人员 ID。

    【讨论】:

    • 这非常有帮助!很遗憾,它还没有内置到 SQL Server 中。
    【解决方案2】:

    排名在索引中是无用的,您不能合并它们并期望结果意味着任何东西。每个索引的排名数字是苹果/橙子/葡萄/西瓜/对的比较,没有相对意味着其他索引的WRT内容。

    当然,您可以尝试在索引之间链接/权重/顺序排名,以尝试伪造一个有意义的结果,但最终结果仍然是胡言乱语,但可能仍然足够好,可以根据具体情况提供可行的解决方案你的情况。

    在我看来,最好的解决方案是将您想要搜索的所有数据放在单个 FTS 索引/列中,并使用该列的排名来对您的输出进行排序。即使您必须复制字段内容来完成结果。

    【讨论】:

      【解决方案3】:

      就在几周前,我正在解决非常相似的问题,并且解决方案非常简单(尽管丑陋且占用空间)。 按此顺序创建另一个包含 FirstName + FirstName + LastName + MiddleName 组合值的列。重复的 FirstName 列不是拼写错误,这是在搜索期间强制 FT 将 FirstName 中的值权重更高的技巧。

      【讨论】:

        【解决方案4】:

        这样怎么样:

            SELECT p.* from Person p
        left join ContainsTable(Person, FirstName, @SearchValue) firstnamefilter on firstnamefiler.key = p.id
        left join ContainsTable(Person, MiddleName, @SearchValue) middlenamefilter on middlenamefilter.key = p.id
        where (firstnamefilter.rank is not null or middlenamefilter.rank is not null)
        order by firstnamefilter.rank desc, middlenamefilter.rank desc
        

        这将为每个 Person 记录生成一个记录,其中名字或中间名(或两者)与搜索词匹配,并按所有匹配的名字排序(按降序排列),然后是与中间名的所有匹配项(再次按降序排列)

        【讨论】:

          【解决方案5】:

          我假设返回的数据已连接到您架构中的其他表?我会根据从关联数据到全文索引的列开发自己的 RANK。这也保证了 RANK 值的准确性。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-09-06
            • 1970-01-01
            • 2011-06-10
            • 2011-10-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多