1

我在 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列,从而获得更高的分数匹配包括人名和姓氏的字符串?

4

2 回答 2

2

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

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

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

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

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

于 2013-07-01T15:06:00.710 回答
0

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

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

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查询来连接单独的查询,每个查询都使用一个连接,CONTAINSTABLECONTAINSTABLE在同一个查询中加入两个谓词。这意味着使用外部联接而不是内部联接,因此我使用WHERE子句从基表中排除与任一全文索引都不匹配的任何记录。我承认,与使用单个全文索引谓词UNION生成单个结果集的单独查询相比,我没有对它的执行情况进行任何检查。
  • 虽然不能保证索引视图中完整搜索文本的匹配排名会高于基表列的全文索引中单个单词的匹配排名,因为排名值是任意的,但我的测试到目前为止已经表明,在实践中它总是(到目前为止!)。
于 2018-11-01T17:27:25.007 回答