优化以通配符

时间:2017-01-26 17:09:07

标签: sql sql-server indexing wildcard sql-like

我在SQL Server数据库中有一个表,其中包含一个地址字段(例如,1 Farnham Road,Guildford,Surrey,GU2XFF),我想在搜索字符串之前和之后使用通配符进行搜索。

SELECT *
FROM Table
WHERE Address_Field LIKE '%nham%'

我在此表中有大约200万条记录,我发现查询需要5到10秒,这不是理想的。我相信这是因为前面的通配符。

我认为我说的是,由于前面的通配符,任何索引都不会用于搜索操作。

使用全文搜索和CONTAINS是不可能的,因为我想搜索单词的后半部分(我知道你可以在下面的查询中替换Guil *的搜索字符串,这将返回结果)。当然运行以下命令不会返回结果

SELECT *
FROM Table
WHERE CONTAINS(Address_Field, '"nham"')

有没有办法优化前面的通配符查询?

3 个答案:

答案 0 :(得分:4)

这是一个(不是真正推荐的)解决方案。

创建表格AddressSubstrings。该表每个地址有多行,主键为table

将地址插入table时,从每个位置开始插入子字符串。所以,如果你想插入'abcd',那么你会插入:

  • ABCD
  • BCD
  • CD
  • d

以及表中行的唯一ID。 (这可以使用触发器完成。)

AddressSubstrings(AddressSubstring)上创建索引。

然后,您可以将查询短语描述为:

SELECT *
FROM Table t JOIN
     AddressSubstrings ads
     ON t.table_id = ads.table_id
WHERE ads.AddressSubstring LIKE 'nham%';

现在将有一个以nham开头的匹配行。因此,like应该使用索引(并且全文索引也可以使用)。

如果您对正确方式感兴趣来处理此问题,那么合理的起点是Postgres documentation。这使用类似于上面的方法,但使用n-gram。对于您的特定问题,n-gram的唯一问题是它们需要重写比较以及更改存储。

答案 1 :(得分:3)

我无法为这个棘手的问题提供完整的解决方案。

但是,如果您正在寻找创建后缀搜索功能,例如,您可以在其中找到包含ilsonABC123000654行的行包含654 WHERE REVERSE(textcolumn) LIKE REVERSE('ilson') + '%' 的{​​{1}},这是一个建议。

ABC123000654

当然,这不是sargable我在这里写的方式。但是许多现代DBMS(包括最新版本的SQL Server)允许对计算列或虚拟列进行定义和索引。

我已经在具有大量记录ID的医疗保健系统(例如p+1)中为最终用户带来了这种技术的喜悦。

答案 2 :(得分:1)

并非没有认真的准备工作,hwilson1。

冒着重复显而易见的风险 - 任何搜索路径优化 - 导致决定是否使用索引,或使用哪种类型的连接运算符等(独立于我们正在谈论的DBMS) - 工作等于(等于)或范围检查(大于和小于)。

使用领先的通配符,你运气不好。

如前所述,解决方法是一项认真的准备工作:

归结为Vertica的文本搜索功能,该问题已解决。见这里:

https://my.vertica.com/docs/8.0.x/HTML/index.htm#Authoring/AdministratorsGuide/Tables/TextSearch/UsingTextSearch.htm

对于任何其他数据库平台,包括MS SQL,您必须手动执行此操作。

简而言之:它依赖于要优化其文本搜索的表的主键或唯一标识符。

您创建一个辅助表,其主键是基表的主键,加上一个序列号,以及一个VARCHAR列,它将包含您最初使用通配符搜索的基表字符串的一系列子字符串。以过于简化的方式:

如果你的输入表(只显示重要的列)是这样的:

id    |the_search_col                           |other_col
    42|The Restaurant at the End of the Universe|Arthur Dent
    43|The Hitch-Hiker's Guide to the Galaxy    |Ford Prefect

您的辅助搜索表可能包含:

id   |seq|search_token
   42|  1|Restaurant
   42|  2|End
   42|  3|Universe
   43|  1|Hitch-Hiker
   43|  2|Guide
   43|  3|Galaxy

通常情况下,您可以抑制典型的“填充物”,如文章和介词以及撇号,并分成由标点符号和空格分隔的标记。但是,对于你的'%nham%'例子,你可能需要和一位专门从事英语形态学的语言学家交谈才能找到分裂的候选人......: - ]

你可以从我在没有PIVOT子句的情况下解除水平系列度量时使用的相同技术开始,如下所示:

Pivot sql convert rows to columns

然后,使用可能嵌套的CHARINDEX()和SUBSTRING()的组合,使用从CROSS JOIN获得的索引和一系列索引整数,如上面建议的帖子所述,并使用该索引作为辅助搜索表的序列。

search_token上放置一个索引,你将拥有一张非常快速的大表访问路径。

在公园里不是漫步,我同意,但很有希望......

快乐的比赛 -

Marco the Sane