【问题标题】:Full text searching scores across multiple columns跨多个列的全文搜索分数
【发布时间】:2026-02-13 05:30:01
【问题描述】:

我在 SQL Server 数据库上使用全文搜索来返回来自多个表的结果。最简单的情况是搜索人名字段和描述字段。我用来执行此操作的代码如下所示:

select t.ProjectID as ProjectID, sum(t.rnk) as weightRank
from
(
 select KEY_TBL.RANK * 1.0 as rnk, FT_TBL.ProjectID as ProjectID
 FROM Projects as FT_TBL 
 INNER JOIN FREETEXTTABLE(Projects, Description,  @SearchText) AS KEY_TBL 
   ON FT_TBL.ProjectID=KEY_TBL.[KEY]

     union all

 select KEY_TBL.RANK * 50 as rnk, FT_TBL.ProjectID as ProjectID
 FROM Projects as FT_TBL 
 ... <-- complex unimportant join
     INNER JOIN People as p on pp.PersonID = p.PersonID
 INNER JOIN FREETEXTTABLE(People, (FirstName, LastName), @SearchText) AS KEY_TBL 
    ON p.PersonID=KEY_TBL.[KEY]
 )
group by ProjectID

正如上面(希望)清楚的那样,我试图在项目描述字段中重视一个人的姓名匹配而不是匹配。如果我搜索“john”之类的内容,那么所有以 john 为名的项目都会得到很大的权重(如预期的那样)。我遇到的问题是搜索有人提供了像“约翰史密斯”这样的全名。在这种情况下,名称的匹配度要弱得多,因为(我认为)在每个 firstname / lastname 列中只有一半的搜索词匹配。在许多情况下,这意味着与输入的姓名完全匹配的人不一定会在搜索结果顶部附近返回。

我已经能够通过分别搜索每个 firstname / lastname 字段并将它们的分数加在一起来纠正此问题,因此我的新查询如下所示:

select t.ProjectID as ProjectID, sum(t.rnk) as weightRank
from
(
 select KEY_TBL.RANK * 1.0 as rnk, FT_TBL.ProjectID as ProjectID
 FROM Projects as FT_TBL 
 INNER JOIN FREETEXTTABLE(Projects, Description,  @SearchText) AS KEY_TBL 
   ON FT_TBL.ProjectID=KEY_TBL.[KEY]

     union all

 select KEY_TBL.RANK * 50 as rnk, FT_TBL.ProjectID as ProjectID
 FROM Projects as FT_TBL 
 ... <-- complex unimportant join
     INNER JOIN People as p on pp.PersonID = p.PersonID
 INNER JOIN FREETEXTTABLE(People, (FirstName), @SearchText) AS KEY_TBL 
    ON p.PersonID=KEY_TBL.[KEY]

     union all

 select KEY_TBL.RANK * 50 as rnk, FT_TBL.ProjectID as ProjectID
 FROM Projects as FT_TBL 
 ... <-- complex unimportant join
     INNER JOIN People as p on pp.PersonID = p.PersonID
 INNER JOIN FREETEXTTABLE(People, (LastName), @SearchText) AS KEY_TBL 
    ON p.PersonID=KEY_TBL.[KEY]
 )
group by ProjectID

我的问题:

这是我应该采用的方法,还是有某种方法可以让全文搜索对列列表进行操作,就好像它是一个文本块一样:即将firstnamelastname 列视为单个name 列,导致包含人员名字和姓氏的字符串的得分更高?

【问题讨论】:

    标签: sql-server full-text-search freetexttable


    【解决方案1】:

    我最近遇到了这个问题,我使用一个计算列将所需的列连接到一个字符串中,然后在该列上创建全文索引。

    我通过复制计算列中的加权字段来实现加权。

    即姓氏出现 3 次,名字出现一次。

    ALTER TABLE dbo.person ADD
    PrimarySearchColumn AS 
    COALESCE(NULLIF(forename,'') + ' ' + forename + ' ', '') +
    COALESCE(NULLIF(surname,'')  + ' ' + surname  + ' ' + surname  + ' ', '') PERSISTED
    

    您必须确保使用持久化关键字,以便不会在每次读取时计算列。

    【讨论】:

    • 忘记对此发表评论。我对这个问题采取了稍微不同的方法,并且将来会用它来更新我的问题——但这对我来说也很有效。谢谢!
    • 我发现使用单独列的一个问题是,为每列生成的排名不是基于相同的数字。对于相同或相似的搜索,它们可能完全不同。一列的排名可能在 700 到 800 之间,另一列的排名在 10 到 20 之间。您可以运行存储过程来调查生成的排名。
    • 感谢您的反馈 - 我忘记了。
    【解决方案2】:

    我知道这是一个老问题,但我遇到了同样的问题并以不同的方式解决了它。

    我创建了包含组合字段的索引视图,而不是向原始表添加计算列(这可能并不总是一种选择)。使用原始示例:

    CREATE VIEW [dbo].[v_PeopleFullName]
    WITH SCHEMABINDING 
    AS SELECT dbo.People.PersonID, ISNULL(dbo.People.FirstName + ' ', '') + dbo.People.LastName AS FullName
    FROM dbo.People
    GO
    
    CREATE UNIQUE CLUSTERED INDEX UQ_v_PeopleFullName
    ON dbo.[v_PeopleFullName] ([PersonID])
    GO
    

    然后我在查询中加入该视图,以及基表中各个列上的现有全文谓词,以便我可以在各个列中找到完全匹配和部分匹配,如下所示:

    DECLARE @SearchText NVARCHAR(100) = ' "' + @OriginalSearchText + '" ' --For matching exact phrase
    DECLARE @SearchTextWords NVARCHAR(100) = ' "' + REPLACE(@OriginalSearchText, ' ', '" OR "') + '" ' --For matching on words in phrase
    
    SELECT FT_TBL.ProjectID as ProjectID, 
        ISNULL(KEY_TBL.[Rank], 0) + ISNULL(KEY_VIEW.[Rank], 0) AS [Rank]
    FROM Projects as FT_TBL 
    INNER JOIN People as p on FT_TBL.PersonID = p.PersonID
    LEFT OUTER JOIN CONTAINSTABLE(People, (FirstName, LastName), @SearchTextWords) AS KEY_TBL ON p.PersonID = KEY_TBL.[KEY] INNER JOIN
    LEFT OUTER JOIN CONTAINSTABLE(v_PeopleFullName, FullName, @SearchText) AS KEY_VIEW ON p.PersonID = KEY_VIEW.[Key]
    WHERE ISNULL(KEY_TBL.[Rank], 0) + ISNULL(KEY_VIEW.[Rank], 0) > 0
    ORDER BY [Rank] DESC
    

    对此的一些说明:

    • 我使用的是CONTAINSTABLE 而不是FREETEXTTABLE,因为它似乎更适合我搜索姓名。当我搜索的是名称时,我对查找具有相似含义的词或词的变化形式不感兴趣。
    • 因为我使用的是CONTAINSTABLE,所以我必须对@SearchText 变量进行一些预处理,以使其兼容并使用OR 运算符将其分解为单个单词,以便在基础上进行搜索表的全文索引。
    • 而不是使用UNION 查询来连接单独的查询,每个查询都使用单个连接的CONTAINSTABLE 我在同一查询中加入两个CONTAINSTABLE 谓词。这意味着使用外连接而不是内连接,因此我使用WHERE 子句从基表中排除与任一全文索引都不匹配的任何记录。我承认,与使用单个全文索引谓词 UNIONised 生成单个结果集的单独查询相比,我没有对其性能进行任何检查。
    • 虽然不能保证索引视图中完整搜索文本的匹配排名会高于基表列上全文索引中单个单词的匹配排名,因为排名值是任意的,但我的到目前为止的测试表明,在实践中它总是(到目前为止!)。

    【讨论】: