1

我有一张包含公司的表格和一张带有类别的表格。我正在使用 SQL Server 自由文本搜索,并且搜索公司(按名称和描述)工作正常。但现在我还想包括类别表。

我想搜索类似的东西:ABC 24 Supermarket.

现在,ABC 24应该与表Name中的列进行匹配company,并且Supermarketcategory该公司所连接的名称。

现在我有这样的事情:

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.[Description], CO.[Name]), @SearchString)
AND CONTAINS(CA.[Description], @SearchString)

但这当然没有给我任何东西,因为在公司或类别表中都找不到我的搜索字符串。有没有人知道如何在我的公司和类别表上进行组合搜索?

正如下面 Lobo 的回答中所建议的那样,拆分字符串的想法并不是一个真正的选择。因为我不知道哪个部分应该匹配一个类别,哪个部分应该用于匹配公司名称/描述。用户不妨输入“Supermarket ABC 24”。

4

9 回答 9

4

恕我直言,这样做的正确方法是创建一个索引视图,其中包含主表的主键(在您的示例中为“公司”)和包含您实际想要搜索的所有内容的第二列,即

create View View_FreeTextHelper with schemabinding as
select CO.PrimaryKey,                            -- or whatever your PK is named
       CO.description +' '+CA.description +' '+CO.whatever as Searchtext
  from dbo.company CO join 
       dbo.category CA on CA.CategoryId = CO.CategoryId

请注意表格的两部分形式。一些限制由此产生,例如所有涉及的表必须在同一个表空间中,据我所知,在这种连接中不允许使用 TEXT 列(尽管您可以强制转换它们)。

PrimaryKey现在在列上创建一个唯一索引

create unique clustered index [View_Index] 
    on View_FreeTextHelper (PrimaryKey ASC)

最后,使用“Searchtext”列作为要索引的唯一列,在视图上创建全文索引。当然,您可以添加更多列,例如,如果您希望在搜索公司名称和位置以及经理姓名时进行区分(您只需将它们连接到第二列中)。

检索您的数据现在很容易:

select tbl.RANK,
       co.* 
  from freetextTable(View_FreeTextHelper,Search,'Your searchtext here') tbl
  join company co on tbl.key=co.PrimaryKey
 order by tbl.RANK desc

您也可以使用select top 50asfreetexttable子句限制输出,最终将返回相当多的接近结果和不太接近的结果。

最后,如果您找不到“现成公司”之类的内容,请不要感到困惑。当心停止列表。这些是非常常见的单词列表,没有语义用途(如the),因此从要索引的文本中删除。为了包含它们,您必须切换停止列表功能。

最后一点提示:全文非常强大,但有很多功能、技巧和注意事项。完全理解这些技术并获得您想要的最佳结果需要相当多的时间。

玩得开心。

于 2013-09-18T07:02:14.690 回答
1

如果我们假设每行列的名称是唯一的,那么您可以使用以下查询。以下示例返回每列中包含短语“ABC”、“24”或“Supermarket”的所有行

DECLARE @SearchString nvarchar(100) = N'ABC 24 Supermarket'
SET @SearchString = REPLACE(LTRIM(RTRIM(@SearchString)), ' ', '|')
SELECT *
FROM Company CO JOIN Category CA ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS(CO.[Name], @SearchString)
 AND CONTAINS(CO.[Description], @SearchString)
 AND CONTAINS(CA.[Description], @SearchString)

首先,您需要为 WHERE 子句中使用的 CONTAINS 谓词准备一个搜索值。在这种情况下,我替换了“|”上的单词之间的空格 逻辑运算符(可以使用条形符号 (|) 代替 OR 关键字来表示 OR 运算符。)

于 2013-09-19T13:51:58.743 回答
0

我没有安装 sql-server 可以尝试CONTAINS。你可以替换为column LIKE '%string%'CONTAINS(column, 'string')尝试。

在此处查看所有查询。


另一个更新- 在阅读其他答案和手册之后,您似乎不需要包含字符串中的括号值,这与我预期的不同。所以这也应该有效 - (你甚至可以尝试' | '而不是' OR '

SELECT CO.name, CA.description FROM company CO
         INNER JOIN category CA
             ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name,CO.description), REPLACE('ABC 25 SuperMarket', ' ', ' OR '))
      AND
      CONTAINS(CA.description, REPLACE('ABC 25 SuperMarket', ' ', ' OR '))  

如果它在替换附近抱怨语法错误,您可以创建一个搜索字符串DECLARE @SearchString varchar(MAX) = REPLACE('ABC 25 SuperMarket',' ', ' OR '),然后将其replace(......)用作第二个参数。


根据您修改后的问题进行更新-

首先,如果可能,您应该将逻辑移动到应用程序级别。我想,在这里处理它太多了。我提出了这个查询,但请注意,这将拆分每个单词并在两者中搜索它,name因此description您最终会得到比您想象的更多的结果。例如,与在我之前的查询中仅返回一个名称相比,这将返回所有名称中具有SupermarketABC名称的所有内容。这实际上应该可以帮助您,因为根据您的说法,用户可能只需键入24SupermarketABC 24"ABC Supermarket 24" or "24 ABC Supermarket" or ...

DECLARE @SearchString varchar(MAX) = 'ABC 24 SuperMarket'
DECLARE @separator varchar(MAX) = ' '
DECLARE @Like1 varchar(MAX) = 'CO.name LIKE'
DECLARE @Like2 varchar(MAX) = 'CA.description LIKE'

DECLARE @WHERE1 varchar(MAX) = '( ' + @Like1 + ' ''%' + 
           REPLACE(@SearchString,@separator,'%'' OR ' + @Like1 + ' ''%')+'%'')'

DECLARE @WHERE2 varchar(MAX) = '( ' + @Like2 + ' ''%' + 
           REPLACE(@SearchString,@separator,'%'' OR ' + @Like2 + ' ''%')+'%'')'

DECLARE @QueryString varchar(MAX) =
           CONCAT('SELECT CO.name, CA.description FROM company CO
                       INNER JOIN category CA
                             ON CA.CategoryId = CO.CategoryId
                    WHERE ', @WHERE1, ' AND ', @WHERE2)
exec(@QueryString);

如果你输出@WHERE1你应该看到

( CO.name LIKE '%ABC%' OR CO.name LIKE '%25%' OR CO.name LIKE '%SuperMarket%')

正如我之前所说,您可能想尝试使用CONTAINS带括号的值,例如

DECLARE @SearchString varchar(MAX) = 'ABC 25 SuperMarket'
DECLARE @separator varchar(MAX) = ' '

DECLARE @WHEREString varchar(MAX) = '''"' + 
        REPLACE(@SearchString, @separator, '" OR "')+'"'''

SELECT CO.name, CA.description FROM company CO
        INNER JOIN category CA
           ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name,CO.description), @WHEREString)
      AND 
      CONTAINS(CA.description, @WHEREString)

如果你输出@WHEREString你应该看到

'"ABC" OR "25" OR "SuperMarket"'

上一个答案:

这将假定最后一个空格之后的单词是 the description,其余的是 `name。

您可以拆分搜索字符串并使用它们,如下所示。like由于我没有安装 sql-server,因此正在使用此查询。

DECLARE @SearchString VARCHAR(100) = 'ABC 24 Supermarket'
DECLARE @searchLength int = len(@SearchString)
DECLARE @searchReverse VARCHAR(100) = reverse(@SearchString)

SELECT CO.name, CA.description FROM company CO
        INNER JOIN category CA
            ON CA.CategoryId = CO.CategoryId
WHERE CO.name LIKE concat( '%', SUBSTRING(@SearchString,0,@searchLength-charindex(' ',@searchReverse)+1), '%')
     AND 
     CA.description LIKE concat( '%', SUBSTRING(@SearchString,@searchLength-charindex(' ',@searchReverse)+2,@searchLength), '%')

这应该有效。请注意 where 子句使用 AND 而不是 OR。

DECLARE @SearchString VARCHAR(100) = 'ABC 24 Supermarket'
DECLARE @searchLength int = len(@SearchString)
DECLARE @searchReverse VARCHAR(100) = reverse(@SearchString)
DECLARE @company VARCHAR(100) = SUBSTRING(@SearchString,0,@searchLength-charindex(' ',@searchReverse)+1)
DECLARE @category VARCHAR(100) = SUBSTRING(@SearchString,@searchLength-charindex(' ',@searchReverse)+2,@searchLength)
    
SELECT CO.name, CA.description FROM company CO
        INNER JOIN category CA
           ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name, CO.description), @company)
      AND
      CONTAINS(CA.description , @category)
于 2013-09-08T19:27:33.133 回答
0

我不知道这是否会构成一个很好的答案(我倾向于怀疑它),但我想解决一个问题,我很高兴选择你的,所以这是我的解决方案:

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'

DECLARE @AllItems table (
  SearchItem varchar(100)
  ),
        @x int;

-- break up @SearchString into component search items:
select @x = charindex(' ', @SearchString);
while @x > 0 begin
  insert @AllItems (SearchItem) values (substring(@SearchString, 1, @x - 1));
  select @SearchString = substring(@searchstring, @x + 1);
  select @x = charindex(' ', @Searchstring);
end;
-- add the last item
insert @AllItems (SearchItem) values (@SearchString)


DECLARE @this varchar(100),  -- = current search item
        @found table (       -- table to contain rows matching the current search item
          ID int
          ),
        @usable table (      -- table to contain rows matching all search items
          ID int             --   already tested
          );

--now process search items one-at-a-time
while (select count(*) from @AllItems) > 0 begin
  select @this = min(SearchItem) from @AllItems;
  delete @AllItems where SearchItem = @this;

  if (select count(*) from @usable) = 0 begin  --first search item
    --for the first item, just find the companies matching this item, in either the 
    --company name or description or category description columns:
    insert @found (ID)
    select CO.CompanyID
    from Company CO inner join Category CA on CO.CategoryID = CA.CategoryID
    where contains ((CO.[Description], [CO.[Name]) @this)
      or contains (CA.[Description], @this)
  end 
  else begin                                   --other search items
    -- for subsequent items, its got to match with the company name or description
    -- or category description as above - BUT it's also got to be a company we 
    -- already identified when processing the previous term
    insert @found (ID)
    select CO.CompanyID
    from Company CO inner join Category CA on CO.CategoryID = CA.CategoryID inner join @usable U on CO.CompanyID = U.ID
    where contains ((CO.[Description], [CO.[Name]) @this)
      or contains (CA.[Description], @this)
  end 

  --now clear out and re-populate the usable companies table ready for processing the
  --next search item
  delete @usable;
  insert @usable (ID)
  select ID
  from @found;

  --and clear out the current matches table, ready for the next search item
  delete @found;
end;


--whatever is in @usable now, is a match with all the component search items, so:
select CO.*
from Company CO inner join Category CA on CO.CategoryId = CA.CategoryId inner join @usable U on CO.CompanyID = U.ID;
于 2013-09-13T05:23:04.403 回答
0

我突然想到,虽然我之前写的答案应该可以正常工作并且相当有效,但一次处理一个搜索项目并且只在现有搜索结果中搜索第一个之后的项目,这样会更快使用动态 sql 一次性完成所有操作。

因此,这是您问题的另一个潜在解决方案,我将其作为单独的答案输入,因为它与我已经发布的解决方案无关:

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'

DECLARE @AllItems table (
  SearchItem varchar(100)
  ),
        @x    int,
        @cmd  varchar(1000),
        @wc   varchar(8000),
        @this varchar(100);

-- break up @SearchString into component search items:
select @x = charindex(' ', @SearchString);
while @x > 0 begin
  insert @AllItems (SearchItem) values (substring(@SearchString, 1, @x - 1));
  select @SearchString = substring(@searchstring, @x + 1);
  select @x = charindex(' ', @Searchstring);
end;
-- add the last item
insert @AllItems (SearchItem) values (@SearchString)

select @cmd = 'select CO.* from Company CO inner join Category CA on CO.CategoryId = CA.CategoryId WHERE';


--now process search items one-at-a-time building up a where clause to plug into @cmd:
while (select count(*) from @AllItems) > 0 begin
  select @this = min(SearchItem) from @AllItems;
  delete @AllItems where SearchItem = @this;

  select @wc = @wc +
    'AND (contains ((CO.[Description], [CO.[Name]) ''' + @this + ''') or contains (CA.[Description], ''' + @this + ''') '
end;

--ready to go:
exec (@cmd + substring(@wc, 4));  --substring removes first AND
于 2013-09-13T06:06:08.480 回答
0

这里的想法是我们要将字符串解析为不同的变量,然后搜索其他变量以进行匹配。

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"', @A varchar(100), @B varchar(100), @C varchar(100),@index int

set @A = Substring(@searchString, 1, PATINDEX('% %', @searchString) -1)
set @index = PATINDEX('% %', @searchString) + 1
set @B = Substring(@searchString, @index, PATINDEX('% %', @substring(@searchstring, @index, 100)) -1)
Set @index = PATINDEX('% %', @substring(@searchstring, @index, 100)) + 1
set @C = Substring(@searchString, @index, PATINDEX('% %', @substring(@searchstring, @index, 100)) -1)

SELECT * FROM Company CO
INNER JOIN Category CA
  ON CA.CategoryId = CO.CategoryId
WHERE CO.[Description] like @A
  or CO.[Description] like @B
  or CO.[Description] like @c
  or CO.[Name] like @A
  or CO.[Name] like @B
  or CO.[Name] like @C
  or CA.[Description] like @A
  or CA.[Description] like @B
  or CA.[Description] like @C

这段代码对我来说看起来很难看,但它应该满足用户输入最多 3 个要搜索的项目的要求。有人有清理它的建议吗?

于 2013-09-18T19:00:57.703 回答
0

这应该有效。

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
set @SearchString = replace(@SearchString,' ','" or "')

SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.[Description], CO.[Name]), @SearchString)
AND CONTAINS(CA.[Description], @SearchString)

希望有点帮助

于 2013-09-19T17:26:30.173 回答
0

你为什么不把你的逻辑颠倒过来?您的示例试图在您的字段值中查找搜索字符串,但您真正想要做的是在您的搜索字符串中查找您的字段值,不是吗?

    DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE (CONTAINS(@SearchString, CO.[Description]) OR CONTAINS(@SearchString, CO.[Name]))
AND CONTAINS(@SearchString, CA.[Description])
于 2013-09-19T19:18:26.773 回答
-1

您可以使用 FREETEXT 而不是 CONTAIN。

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE FREETEXT((CO.[Description], CO.[Name]), @SearchString)
OR FREETEXT(CA.[Description], @SearchString)
于 2013-09-17T08:17:44.613 回答