我有一个公司的表和一个类别的表。我正在使用SQL Server自由文本搜索,搜索公司(按名称和描述)工作正常。但现在我还要包括类别表。
我想搜索类似的内容:ABC 24 Supermarket
。
现在,ABC 24
应该与Name
表格中的company
列匹配,而Supermarket
是该公司category
的名称连接到。
现在我有这样的事情:
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”。
答案 0 :(得分: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 50
限制输出,因为freetexttable
子句最终会返回非常接近且不太接近的结果。
最后,如果你找不到像'现成的公司'这样的东西,不要感到困惑。小心停止列表。这些是非常常见的单词列表,没有语义用法(如the
),因此从要编入索引的文本中删除。要包含它们,您必须切换停止列表功能。
最后一个tipp:全文非常强大,但有很多功能,技巧和警告。完全理解这些技术并获得您想要的最佳结果需要相当多的时间。
玩得开心。
答案 1 :(得分:1)
如果我们假设每行的列名是唯一的,那么您可以使用下面的查询。以下示例返回在每列中包含短语“ABC”,“24”或“超市”的所有行
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运算符。)
答案 2 :(得分: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
中搜索它,这样您最终会得到比您想象的更多的结果。对于例如这将返回名称中包含Supermarket
或ABC
的所有24
,而不是在我之前的查询中只返回一个名为Supermarket
的{{1}}。这实际上应该可以帮到你,因为根据你的说法,用户可能只需输入ABC 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"'
,其余是`name。
您可以拆分搜索字符串并使用它们,如下所示。此查询使用description
,因为我没有安装sql-server。
like
这应该有效。请注意,where子句使用的是AND而不是OR。
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), '%')
答案 3 :(得分: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;
答案 4 :(得分: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
答案 5 :(得分: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个要搜索的项目的要求。有人建议清理它吗?
答案 6 :(得分: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)
希望有所帮助
答案 7 :(得分: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])
答案 8 :(得分:-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)