优化以通配符开头的 LIKE 表达式
Optimising LIKE expressions that start with wildcards
我在 SQL 服务器数据库中有一个 table,其中有一个地址字段(例如 1 Farnham Road, Guildford, Surrey, GU2XFF),我想在其前后使用通配符进行搜索搜索字符串。
SELECT *
FROM Table
WHERE Address_Field LIKE '%nham%'
我在这个 table 中有大约 200 万条记录,我发现查询需要 5-10 秒,这并不理想。我相信这是因为前面的通配符。
由于前面的通配符,我认为我说任何索引都不会用于查找操作是正确的。
无法使用全文搜索和 CONTAINS,因为我想搜索单词的后半部分(我知道您可以在下面的查询中替换 Guil* 的搜索字符串,这样 return 结果)。当然运行以下return没有结果
SELECT *
FROM Table
WHERE CONTAINS(Address_Field, '"nham"')
有什么方法可以优化前面带通配符的查询吗?
这是一个(不是真正推荐的)解决方案。
创建一个tableAddressSubstrings
。 table 每个地址和 table
.
的主键会有多行
当您将地址插入 table
时,从每个位置开始插入子字符串。所以,如果你想插入 'abcd',那么你会插入:
- abcd
- bcd
- 光盘
- d
以及 Table 中行的唯一 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-grams。对于您的特定问题,n-grams 的唯一问题是它们需要 re-writing 比较以及更改存储。
hwilson1,不是没有认真的准备工作。
冒着重复明显的风险 - 任何搜索路径优化 - 导致决定是否使用索引,或使用哪种类型的连接运算符等(独立于我们正在谈论的 DBMS) - 适用于相等(等于)或范围检查(greater-than 和 less-than)。
有前导通配符,你就倒霉了。
解决方法是认真的准备工作,如前所述:
归结为 Vertica 的 文本搜索 功能,解决了该问题。看这里:
对于任何其他数据库平台,包括 MS SQL,您必须手动执行此操作。
简而言之:它依赖于您要优化其文本搜索的 table 的主键或唯一标识符。
你创建一个辅助table,它的主键是你的基table的主键,加上一个序列号,以及一个包含一系列基的子字符串的VARCHAR列table 您最初使用通配符搜索的字符串。 over-simplified 方式:
如果您的输入 table(仅显示重要的列)是这样的:
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
您的辅助搜索 table 可能包含:
id |seq|search_token
42| 1|Restaurant
42| 2|End
42| 3|Universe
43| 1|Hitch-Hiker
43| 2|Guide
43| 3|Galaxy
通常,您会抑制典型的 "fillers",例如冠词和介词以及 apostrophe-s,并拆分为由标点符号和白色 space 分隔的标记。但是,对于您的 '%nham%' 示例,您可能需要与专门研究英语词法的语言学家交谈以找到拆分标记候选....:-]
您可以从我 un-pivot 没有 PIVOT 子句的水平系列度量时使用的相同技术开始,如下所示:
然后,结合使用可能嵌套的 CHARINDEX() 和 SUBSTRING(),使用您从 CROSS JOIN 获得的索引以及我上面建议的 post 中描述的一系列索引整数,以及使用该索引作为辅助搜索的序列 table.
在 search_token
上放置一个索引,您将有一个非常快速的访问路径到一个大的 table。
不是在公园里散步,我同意,但很有希望......
玩的开心 -
理智的马可
我无法为这个难题提供完整的解决方案。
但是,如果您希望创建后缀搜索功能,例如,您可以在其中找到包含 HWilson
和 ilson
的行以及包含 ABC123000654
和 654
,这是一个建议。
WHERE REVERSE(textcolumn) LIKE REVERSE('ilson') + '%'
当然,sargable 这不是我在这里写的方式。但是许多现代 DBMS,包括最近版本的 SQL 服务器,都允许计算列或虚拟列的定义和索引。
我已经在一个 health-care 系统中部署了这项技术,令最终用户高兴的是,该系统具有许多记录 ID,例如 ABC123000654
。
我在 SQL 服务器数据库中有一个 table,其中有一个地址字段(例如 1 Farnham Road, Guildford, Surrey, GU2XFF),我想在其前后使用通配符进行搜索搜索字符串。
SELECT *
FROM Table
WHERE Address_Field LIKE '%nham%'
我在这个 table 中有大约 200 万条记录,我发现查询需要 5-10 秒,这并不理想。我相信这是因为前面的通配符。
由于前面的通配符,我认为我说任何索引都不会用于查找操作是正确的。
无法使用全文搜索和 CONTAINS,因为我想搜索单词的后半部分(我知道您可以在下面的查询中替换 Guil* 的搜索字符串,这样 return 结果)。当然运行以下return没有结果
SELECT *
FROM Table
WHERE CONTAINS(Address_Field, '"nham"')
有什么方法可以优化前面带通配符的查询吗?
这是一个(不是真正推荐的)解决方案。
创建一个tableAddressSubstrings
。 table 每个地址和 table
.
当您将地址插入 table
时,从每个位置开始插入子字符串。所以,如果你想插入 'abcd',那么你会插入:
- abcd
- bcd
- 光盘
- d
以及 Table 中行的唯一 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-grams。对于您的特定问题,n-grams 的唯一问题是它们需要 re-writing 比较以及更改存储。
hwilson1,不是没有认真的准备工作。
冒着重复明显的风险 - 任何搜索路径优化 - 导致决定是否使用索引,或使用哪种类型的连接运算符等(独立于我们正在谈论的 DBMS) - 适用于相等(等于)或范围检查(greater-than 和 less-than)。
有前导通配符,你就倒霉了。
解决方法是认真的准备工作,如前所述:
归结为 Vertica 的 文本搜索 功能,解决了该问题。看这里:
对于任何其他数据库平台,包括 MS SQL,您必须手动执行此操作。
简而言之:它依赖于您要优化其文本搜索的 table 的主键或唯一标识符。
你创建一个辅助table,它的主键是你的基table的主键,加上一个序列号,以及一个包含一系列基的子字符串的VARCHAR列table 您最初使用通配符搜索的字符串。 over-simplified 方式:
如果您的输入 table(仅显示重要的列)是这样的:
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
您的辅助搜索 table 可能包含:
id |seq|search_token
42| 1|Restaurant
42| 2|End
42| 3|Universe
43| 1|Hitch-Hiker
43| 2|Guide
43| 3|Galaxy
通常,您会抑制典型的 "fillers",例如冠词和介词以及 apostrophe-s,并拆分为由标点符号和白色 space 分隔的标记。但是,对于您的 '%nham%' 示例,您可能需要与专门研究英语词法的语言学家交谈以找到拆分标记候选....:-]
您可以从我 un-pivot 没有 PIVOT 子句的水平系列度量时使用的相同技术开始,如下所示:
然后,结合使用可能嵌套的 CHARINDEX() 和 SUBSTRING(),使用您从 CROSS JOIN 获得的索引以及我上面建议的 post 中描述的一系列索引整数,以及使用该索引作为辅助搜索的序列 table.
在 search_token
上放置一个索引,您将有一个非常快速的访问路径到一个大的 table。
不是在公园里散步,我同意,但很有希望......
玩的开心 -
理智的马可
我无法为这个难题提供完整的解决方案。
但是,如果您希望创建后缀搜索功能,例如,您可以在其中找到包含 HWilson
和 ilson
的行以及包含 ABC123000654
和 654
,这是一个建议。
WHERE REVERSE(textcolumn) LIKE REVERSE('ilson') + '%'
当然,sargable 这不是我在这里写的方式。但是许多现代 DBMS,包括最近版本的 SQL 服务器,都允许计算列或虚拟列的定义和索引。
我已经在一个 health-care 系统中部署了这项技术,令最终用户高兴的是,该系统具有许多记录 ID,例如 ABC123000654
。