URL 字段 sql 服务器的全文搜索
Full Text Search of URL field sql server
Objective:Return 所有 URL 以 "https://mywebsite.domain.com/as/product/4/"
开头
鉴于:
- 在 URL 字段上应用了全文搜索。
- SQL服务器版本:2014.
- 20+ 百万行
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 所有以模式开头的记录匹配到第一个通配符 %
。
您可以将 CONTAINS
与 LIKE
子查询一起使用以仅匹配开头:
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/"')
没有过滤值)
经过大量搜索。问题出在 /
。正斜杠不包含在噪音词文件中,但我猜它被归类为定界符或分词符,因此不可搜索。
阅读这些主题:
- experts-exchange Topic
- Whosebug Topic
- Google Groups
EDIT2:
我找到了一个建议的解决方案,即
/
被认为是英文分词器 您可以从注册表中更改它
- 导航到注册表值
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\<InstanceRoot>\MSSearch\Language\eng
和
HKEY_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%'
Objective:Return 所有 URL 以 "https://mywebsite.domain.com/as/product/4/"
鉴于:
- 在 URL 字段上应用了全文搜索。
- SQL服务器版本:2014.
- 20+ 百万行
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 所有以模式开头的记录匹配到第一个通配符 %
。
您可以将 CONTAINS
与 LIKE
子查询一起使用以仅匹配开头:
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/"')
没有过滤值)
经过大量搜索。问题出在 /
。正斜杠不包含在噪音词文件中,但我猜它被归类为定界符或分词符,因此不可搜索。
阅读这些主题:
- experts-exchange Topic
- Whosebug Topic
- Google Groups
EDIT2:
我找到了一个建议的解决方案,即
/
被认为是英文分词器 您可以从注册表中更改它
- 导航到注册表值
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\<InstanceRoot>\MSSearch\Language\eng
和HKEY_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%'