最基本的模式仅由单个字母组成。当与该模式进行比较时,包含这个字母的字符串就是一个“匹配”。例如,如果模式是“a”,则字符串“abcd”就是一个匹配,而字符串“xyz”则不是。正则表达式的强大功能来自于预定义的运算符(也称为元字符),它们可以用很小的空间来表示模式。根据“方言”和受支持的功能,可以使用不同的元字符。通常,其中的一些可用字符如下:
另一个更高级的示例是模式“[a-z]* = ([0-9]|0x00);”。与这个模式相匹配的字符串包含这样的子串:它由几个大写字母、后面跟上一个空格、一个等号、另一个空格,然后是一个数字或字符串“0x00”组成。该子串的最后一个字符必须是分号。使用 perl,这个模式可以表示为“//w* = (//d|0x00);”。“nm = 0x00;”和“x = 7;”是两个可以与该模式匹配的字符串。但字符串“z = 123;”不能匹配,因为 123 是由三个数字所组成的。
示例方案
让我们定义下列清单( 清单 2)并插入几行:
这个 清单及其数据被用于下面的所有示例。
实现 udf
在我的示例实现中,我选择了现有的名为 pcre(perl 兼容的正则表达式,perl-compatible regular expression)的模式匹配引擎。该引擎提供了用来处理模式和执行匹配的 c api。该引擎和查询中所用的 sql 语言之间“缺失的部分”是 udf。该 udf 由两部分组成:
- 在数据库中创建(或注册)该函数的 create function 语句。
- 该函数的主体,它实现了用于正则表达式匹配引擎的 c api 调用的封装器
第二部分由一小段 c 代码组成,它实现了 udf 入口点。在查询执行期间,db2 为每个要与模式匹配的行调用这个入口点。 清单 5中的示例列出了该代码的清单。有关 pcre_*
函数和宏的描述,请参考 pcre 库的文档。有关 c 代码的编译和共享库的构建,请参考 db2 application development guide。
用法示例
下列查询试图从表 strtable
中找出包含注释文本的所有字符串。注释以“#”开头,所以模式是“#”后跟非空文本。
在第二个示例中,我们试图找到这种赋值形式的字符串;即“text = text”。为了进一步缩小范围,我们只查找那些右端为数值的赋值。将十六进制表示法作为有效数值对待。
改进性能
尽管上面的函数按照预期的方式工作,但还可以改进它以获得更佳的性能。注:函数内部的执行完成得越快,db2 处理整个 sql 语句的速度也就越快。
db2 通过使用所谓的“高速暂存(scratchpad)”提供了在 udf 调用之间传递信息的机制。此外,您可以标识特定调用“类型”;即它是对该 udf 的第一次调用、普通调用还是最后一次(最终)调用。使用高速暂存和调用类型,有可能只对模式编译一次,然后将该已编译模式的内部表示法重用于对该 udf 的所有后续调用。在最后一次调用时,释放在处理期间分配的资源。
返回匹配子串
大多数模式匹配引擎提供了一种方法,返回与指定模式或其一部分相匹配的子串。如果想在 sql 中使用这种能力,则必须使用不同的方法来实现匹配函数。给定的字符串可能包含不止一个匹配的子串。例如,当解析类似“abc = 123;”或“def = 'some text';”这样的字符串时,用户可能会希望检索由等号分隔的两个子串。您可以使用模式“//w+//s*=//s*(//d+|'[//w//s] *');”来表示适用于该字符串的语法规则。perl 兼容的正则表达式允许您捕获等号两边的子串。最后,必须将要捕获的子串用括号括起来。我已经用该方式编写了第二个子串,但第一个子串不是这样编写的。用于该用途的最终模式是这样的:
在 db2 中做到这一点的需求可以描述成:为一次 udf 调用返回多个结果。换句话说,就是返回针对模式进行匹配的单个字符串的多个子串。db2 的表函数是完成这一任务的完美工具。
用法示例
表函数可以在类似于如下所示的 select 语句中使用:
下一个查询在结果集中用单独的列而不是单独的行返回这两个子串对。这样,用 sql 语句进一步处理这些字符串以及它们的子串要容易些。该查询使用了公共表表达式(由关键字 with 表示)来确保在整个查询中只对每个字符串进行一次求值,而不是在中间表 s1
和 s2
所需的每次子选择中都进行一次求值。