URL 字段 sql 服务器的全文搜索

Full Text Search of URL field sql server

Objective:Return 所有 URL 以 "https://mywebsite.domain.com/as/product/4/"

开头

鉴于:

URL

https://mywebsite.domain.com/as/product/1/production
https://mywebsite.domain.com/as/product/2/items
https://mywebsite.domain.com/as/product/1/affordability
https://mywebsite.domain.com/as/product/3/summary
https://mywebsite.domain.com/as/product/4/schedule
https://mywebsite.domain.com/as/product/4/resources/summary

查询 1:

WHERE CONTAINS (URL, 'https://mywebsite.domain.com/as/product/4')

结果:

All records returned

查询2(阅读MSDN article后加“*”)

WHERE CONTAINS (URL, '"https://mywebsite.domain.com/as/product/4*"')

结果:

No records returned

如有任何帮助,我们将不胜感激。

使用Like运算符:

WHERE URL LIKE 'https://mywebsite.domain.com/as/product/4%'

% 是一个通配符。这应该 return 所有以模式开头的记录匹配到第一个通配符 %

您可以将 CONTAINSLIKE 子查询一起使用以仅匹配开头:

SELECT * 
FROM (
SELECT * 
FROM myTable WHERE CONTAINS (URL, '"https://mywebsite.domain.com/as/product/4/"')
) AS S1 
WHERE S1.URL LIKE 'https://mywebsite.domain.com/as/product/4/%' 

这样,SLOW LIKE 运算符查询将 运行 针对较小的记录集

EDIT1:(如果 WHERE CONTAINS (URL, '"https://mywebsite.domain.com/as/product/4/"') 没有过滤值)

经过大量搜索。问题出在 / 。正斜杠不包含在噪音词文件中,但我猜它被归类为定界符或分词符,因此不可搜索。

阅读这些主题:

EDIT2:

我找到了一个建议的解决方案,即

/ 被认为是英文分词器 您可以从注册表中更改它

  • 导航到注册表值 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\<InstanceRoot>\MSSearch\Language\engHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\<InstanceRoot>\MSSearch\Language\enu
  • WBreakerClass 的明确值。

Sql 服务器将 https://mywebsite.domain.com/as/product/4 视为一个词。

注意: 以上两条路径我假设您使用英语作为分词器。

在此阅读有关分词器的更多信息MSDN Topic

如果您始终搜索字符串的开头,这将确保优化器可以使用索引。我假设 URL 是 VARCHAR

Declare @p varchar(500) ='https://mywebsite.domain.com/as/product/4'

Declare @maxChar char(1);
select @maxChar = max(ch)
from (
    select top(256) ch = char(row_number() over(order by (select null)) - 1)
    from sys.all_objects) t;
select @maxChar;

-- ..
WHERE URL > @p AND URL < @p + @maxChar

比较字符串时,Sql 服务器将结尾的 space 添加到较短的字符串中。请参阅 https://support.microsoft.com/en-us/kb/316626 . According to http://www.ietf.org/rfc/rfc1738.txt , http://www.ietf.org/rfc/rfc1738.txt 所有允许的 URL 符号均大于 space。因此搜索参数,例如 'https://mywebsite.domain.com/as/product/4',将小于以该参数开头且超过参数长度的任何 URL。

对于类似的问题,我习惯了两种解决方案,具体取决于您的需求,主要是性能或资源或并发性..等..

LIKE 运算符可能是您最好的朋友,它的 table 也非常大。

索引
首先,你需要索引你的 url 列,处理 20+ 百万条记录不是一件容易的事, 索引它可能会花费您 1.5 - 2.0 Gb 的磁盘 space, 但您会在短时间内(毫秒)得到您的查询

使用列上的索引进行搜索,LIKE FixedPattern+% 执行索引查找,您无法进一步改进

第一个解法:

CREATE NONCLUSTERED INDEX [IX_URL] ON [url_table] ([url]);

DECLARE @Domain VARCHAR(100) = 'https://mywebsite.domain.com/'
DECLARE @Path VARCHAR(100) = 'as/product/'
DECLARE @Product VARCHAR(20) = '4'
DECLARE @LikeAll VARCHAR(100) = @Domain + @Path + @Product + '/%'

SELECT url
FROM url_table
WHERE url LIKE @LikeAll

第二种解法
第二个选项有点棘手,但非常有效。
你说 url 的协议和域是固定的,你需要在之后搜索一些东西。
以下是一个技巧,您可以根据自己的需要对其进行微调。
这个想法是向您的 url table 添加一个虚拟(计算)列,然后在其上添加一个索引。
这将大大减少索引维度并提高查询性能,而代价是 insert/update

中的计算开销非常小
ALTER TABLE url_table ADD path AS (SUBSTRING(url, 30, 4000));
CREATE NONCLUSTERED INDEX [IX_PATH] ON [url_table] ([path]);

DECLARE @Domain VARCHAR(100) = 'https://mywebsite.domain.com/'
DECLARE @Path VARCHAR(100) = 'as/product/'
DECLARE @Product VARCHAR(20) = '4'
DECLARE @LikeMid VARCHAR(100) = @Path + @Product + '/%' 

select @Domain + _path -- pay attention!!
FROM url_table
WHERE url LIKE @SrcAll

请注意,我们 selecting @Domain + _path 而不是 url,以避免 table 访问并仅处理索引数据。

如果您需要 url_table 中的其他列,您最好的选择是

declare @l table (id int primary key)
insert  into @l
select id 
from url_table 
where _path like @LikeMid

select url
from url_table
where id in (select id from @l)

非常快

第三种解法
这是第二个的变体。
在您的示例数据中,我看到路径包含 /product/ 后跟一个数字,我假设它是产品编号。 或许你可以考虑以下

ALTER TABLE url_table ADD _product AS (cast(substring(url,nullif(CHARINDEX('/product/',url,29)+9,9), CHARINDEX('/',url,nullif(CHARINDEX('/product/',url,29)+9,9))-nullif(CHARINDEX('/product/',url,29)+9,9)) as bigint));
CREATE NONCLUSTERED INDEX [IX_PRODUCT] ON [url] ([_product]);

select id, url
from url_table 
where _product = 4

这将生成一个产品编号为整数类型的计算列,索引仅为 500Mb,对整数的查询将非常快。
此外,select 来自 url_table 的所有列的开销非常小,因此您可以 SELECT * 几乎没有性能问题。

P.S。 您可以删除全文索引并保存 space 和资源..

SELECT * FROM myTable WHERE URL LIKE 'https://mywebsite.domain.com/as/product/4%'