使 SQL Server 中的正则表达式搜索更高效
Posted
技术标签:
【中文标题】使 SQL Server 中的正则表达式搜索更高效【英文标题】:Making regex search more efficient in SQL Server 【发布时间】:2014-10-17 22:37:34 【问题描述】:我正在尝试创建一个拼字游戏机器人。因此,我将所有(270 万)波兰语单词都扔到了 SQL Server 数据库中,现在正致力于为正则表达式查询创建模式。我刚刚发现在WHERE
子句中添加一些条件可以使搜索效率更高。
例如,执行查询:
SELECT * FROM words WHERE dbo.[like](word, '^[def]1,3$') = 1;
持续约 43 秒,但添加了相当明显的长度条件:
SELECT *
FROM words
WHERE dbo.[like](word, '^[def]1,3$') = 1 AND LEN(word) <= 3;
将执行时间减少到 3 秒...您能告诉我原因,并建议一些有助于提高查询效率的技术吗?
附: like函数是用c#写的CLR:
public static bool Like(string text, string pattern)
Match match = Regex.Match(text, pattern);
return (match.Value != String.Empty);
【问题讨论】:
在您的表上尝试 FULL TEXT 索引:msdn.microsoft.com/en-ca/library/ms187317(v=sql.90).aspx 如果你有 270 万行,SQL Server 的查询优化器不可能知道哪些要检查,哪些不匹配你的正则表达式。但是,如果您添加AND LEN(word) <= 3
,则查询优化器可以排除 4 个或更多字符的所有单词,从而将对正则表达式检查器的调用应用于小得多的单词集 - 这就是为什么查询执行速度更快
一件简单的事情是将CLR函数更改为使用Regex.IsMatch(text, pattern)
。但是,通过将所有单词保存在内存中并使用您的客户语言可能会更好地解决这个问题。在 C# 中,您可以使用 RegEx(pattern)
构造函数来加快速度。您还可以查看诸如尝试之类的专业数据结构
@PieterGeerkens 我认为 CLR 函数不会使用全文索引。
@Laurence:当然——看起来 OP 并没有意识到这种能力,因此已经结束了使用 CLR 和 REGEX。我们的想法是尝试使用全文索引来减少所需的模式匹配。
【参考方案1】:
在 sql 中使用 clr 和 regex 很慢,您对此无能为力。 我的建议是限制您需要运行的正则表达式的数量,并尝试减少运行正则表达式所需的数据量,例如像您在第二个查询中所做的那样。
“显示查询计划”窗口是您优化 sql 查询的朋友。
内置的 sql LIKE 运算符会更快,但当然也受到更多限制。 内置的 LIKE 运算符还具有有时能够使用索引的额外好处。
您可以使用 sql LIKE 将查询重写为如下内容:
SELECT *
FROM words
WHERE
word LIKE '[def][def][def]'
OR word LIKE '[def][def]'
OR word LIKE '[def]'
但它并不漂亮
您可能还会发现这个很有趣: What makes a SQL statement sargable?
【讨论】:
不幸的是,在我的情况下,几乎不可能不使用正则表达式,因为我的项目需要进行比我给出的示例更复杂的查询。我看到的唯一其他解决方案是使用 charindex()、len()、like 等构建 loooong 查询,但它似乎效率不高。但是,正如你所说,我将尝试使用这些函数来限制正则表达式的执行。谢谢你的好回答 我自己也遇到了同样的问题,而且由于它们的灵活性,我还不得不在很多地方使用正则表达式。为了解决性能问题,我必须尽量避免使用它们,并且在某些情况下提前进行正则表达式查找并将结果存储在查找表中以便快速访问。您可能还想将您的 clr 函数标记为确定性和精确性。确定性意味着对于给定的输入,结果总是相同的,这将允许查询优化器在某些情况下缓存结果,并且还允许您将索引视图与函数一起使用。【参考方案2】:根据@Laurence 评论和@user3973227 的回答,我提出了迄今为止最好的解决方案。我尝试将我的字典放入内存,然后用正则表达式过滤它:
var command = new SqlCommand("SELECT word FROM words", con);
SqlDataReader reader = command.ExecuteReader();
Regex regex = new Regex("^[def]1,3$");
while (reader.Read())
if(regex.IsMatch(reader.GetString(0)))
GameOperations.Log(reader.GetString(0));
当我在查询中添加长度条件时,它在 2.6 秒内完成了这项工作:
var command = new SqlCommand("SELECT word FROM words WHERE LEN(word) <= 3", con);
我在 0.09 秒内得到了结果!
证明这种用正则表达式过滤表的方式比任何CLR都要好。为查询添加更多条件也可以加快处理速度,尤其是当它们是 sargable 时(能够使用索引)。使用 Regex 构造函数也很重要,在我的例子中减少了两次过滤的时间。
【讨论】:
以上是关于使 SQL Server 中的正则表达式搜索更高效的主要内容,如果未能解决你的问题,请参考以下文章
带正则表达式的参数化 SQL、ORACLE 与 SQL Server
为了对存储在 Oracle db 中的数据运行搜索查询,在 PL/SQL 中使用 REGEXP 是不是比在 Java 正则表达式中获取所有数据并过滤它更快?