标签:
模式匹配SQL
在一系列的数据行中辨识出某种模式是一种广泛需求的功能,但在以前的SQL中是不可能的。有许多解决方法,但这些都很难写,很难理解,而且执行效率低下。在Oracle数据库中,从12c开始,你可以使用原生SQL中执行效率很高的 MATCH_RECOGNIZE 子句来实现这种功能。本章讨论如何做到这一点,并包括以下几个部分:
.模式匹配的概述
.模式匹配中的基本主题
.模式匹配详细信息
.模式匹配中的高级主题
.模式匹配中的规则与限制
.模式匹配中的例子
模式匹配的概述
SQL中的模式匹配是用MATCH_RECOGNIZE子句执行的。MATCH_RECOGNIZE子句使得你能够执行如下任务:
.使用PARTITION BY 和 ORDER BY 子句对MATCH_RECOGNIZE子句中的数据进行逻辑分区和排序。
.使用MATCH_RECOGNIZE子句中的PATTERN子句来定义要查找的数据行的模式。这些模式使用规则表达式语法,这是一种应用于你所定义的模式变量的强劲而高表现力的功能。
.在DEFINE子句中指定将一行数据映射到一个行模式变量所需的逻辑条件。
.在MEASURES子句中定义度量,这是一些可在SQL查询的其他部分所使用的表达式。
作为模式匹配的一个简单例子,请参见图 20-1 所描绘的股票价格图表。
图 20-1 股票图
模式匹配可以让你识别价格模式,例如图 20-1 中所示的V-形和W-形,还可以进行多种类型的计算。例如,你的计算可能包括观测的次数或者上行或下行的平均值。
这一节包括以下主题:
.为什么使用模式匹配?
.在模式匹配中数据是如何被处理的?
.模式匹配的特殊功能
为什么使用模式匹配?
对于许多类别的工作而言,从不同的数据行中辨识查找模式的能力是很重要的。例子包括受一系列事件驱动的各种业务流程,例如安全应用必须侦测出异常的行为,在金融应用中你会查找价格、交易量和其他行为的模式。其他常见的用途是欺诈检测应用和传感器数据的分析。用来描述这个一般领域的术语是复杂事件处理,而模式匹配是这种活动的一个强大的援助。
现在来考虑例子20-1的查询。它使用在图20-1所示的股票价格,你可以用下面的CREATE和INSERT语句加载到你的数据库。该查询找出所有股票价格跌至底部然后再上涨的情形。这通常被称为V形。在研究这个查询之前,先看看输出。只有三行数据,因为代码被写成仅为每个匹配报告一行,而总共有三次匹配被发现。该MATCH_RECOGNIZE子句允许你在每次匹配展示一行,或者每次匹配展示所有行之间进行选择。在这个例子中,我们使用的是更为简短的每次匹配输出一行。
例子 20-1 模式匹配: 简单的V形,每次匹配输出一行
CREATE TABLE Ticker (SYMBOL VARCHAR2(10), tstamp DATE, price NUMBER);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-01‘, 12);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-02‘, 17);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-03‘, 19);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-04‘, 21);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-05‘, 25);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-06‘, 12);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-07‘, 15);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-08‘, 20);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-09‘, 24);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-10‘, 25);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-11‘, 19);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-12‘, 15);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-13‘, 25);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-14‘, 25);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-15‘, 14);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-16‘, 12);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-17‘, 14);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-18‘, 24);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-19‘, 23);
INSERT INTO Ticker VALUES(‘ACME‘, DATE ‘2011-04-20‘, 22);
SELECT *
FROM Ticker MATCH_RECOGNIZE (
PARTITION BY symbol
ORDER BY tstamp
MEASURES STRT.tstamp AS start_tstamp,
LAST(DOWN.tstamp) AS bottom_tstamp,
LAST(UP.tstamp) AS end_tstamp
ONE ROW PER MATCH
AFTER MATCH SKIP TO LAST UP
PATTERN (STRT DOWN+ UP+)
DEFINE
DOWN AS DOWN.price < PREV(DOWN.price),
UP AS UP.price > PREV(UP.price)
) MR
ORDER BY MR.symbol, MR.start_tstamp;
SYMBOL START_TST BOTTOM_TS END_TSTAM
---------- --------- --------- ---------
ACME 05-APR-11 06-APR-11 10-APR-11
ACME 10-APR-11 12-APR-11 13-APR-11
ACME 14-APR-11 16-APR-11 18-APR-11
这个查询做了什么?下面解释了 MATCH_RECOGNIZE子句中的每一行:
.PARTITION BY 将Ticker表数据划分成逻辑分组,每组包含一种股票代号。
.ORDER BY 将每个逻辑分组内的数据按照tstamp排序。
.MEASURES 定义了三个度量:V形开始的时间戳(start_tstamp),V形底部的时间戳(bottom_tstamp),以及V形结束的时间戳(end_tstamp)。bottom_tstamp 和 end_tstamp 度量使用了LAST()函数来确保读取到的值是每个匹配模式中的最后一个时间戳的值。
.ONE ROW PER MATCH 的意思是对于每个找到的模式匹配,只会有一行输出。
.AFTER MATCH SKIP TO LAST UP 的意思是每当你找到一个匹配,你就在UP模式变量的最后一行重新开始你的搜索。一个模式变量是一个在MATCH_RECOGNIZE中使用的变量,在DEFINE子句定义。
.PATTERN (STRT DOWN+ UP+) 说的是你在搜索的模式有三个模式变量:STRT, DOWN, 以及 UP。DOWN 和 UP之后的加号(+)意思是它们中的每一个都至少有一行被映射。这个模式定义一个规则表达式,这是一种表现力很强的搜索方式。
.DEFINE 给了我们当一个行被映射到你的行模式变量STRT, DOWN, 和 UP时应该满足的条件。因为没有为STRT指定条件,任何一行都可以被映射为STRT。为什么一个模式变量会没有条件?你可以用它来作为测试匹配的起点。DOWN和UP都利用了PREV()函数,这使得它们能够把当前行的价格和前一行的价格进行比较。当价格比前一行更低时DOWN被匹配,所以它定义了V形的下行侧(左腿)。如果价格比前一行更高则被映射到UP。
下面两个图可以帮助你更好地理解例子20-1返回的结果。图20-2显示了映射到特定模式变量(在PATTERN子句中指定)的日期。在模式变量到日期的映射可见之后,MEASURES子句就用该信息来计算度量值。度量值的结果被显示在图20-3中。
图 20-2 股票图演示了哪些日期被映射到哪些模式变量
图20-2标记出映射到模式变量的日期。映射是基于PATTERN子句中指定的模式和DEFINE子句中指定的逻辑条件。垂直的细线显示了被找到的三个匹配模式的边界。在每个匹配中,第一个日期映射到STRT模式变量(标记为Start),紧跟着是一个或多个被映射到DOWN模式变量的日期,最后是一个或多个被映射到UP模式变量的日期。
因为你在查询中指定了AFTER MATCH SKIP TO LAST UP,两个相邻的匹配可以共享一行。这意味着一个单独的日期可以被映射到两个变量。例如,四月十号同时被映射到UP和STRT两个变量:四月十号是第一个匹配的终点,也是第二个匹配的起点。
图20-3 股票图展示了度量值相应的日期
在图20-3中,标记仅仅是查询的MEASURES子句中所定义的度量:START (查询中的start_tstamp), BOTTOM (查询中的bottom_tstamp), 以及 END (查询中的end_tstamp)。如同图20-2,垂直的细线显示了被找到的三个匹配模式的边界。每个匹配有一个起始日期,一个底部日期,以及一个终止日期。正如图20-2,日期四月十号可以在两个匹配中找到:它是第一个匹配的终止度量和第二个匹配的起始度量。图20-3 的标记日期显示了哪些日期对应着度量的定义,这些度量是基于图20-2中所显示的模式变量。
注意,图20-3中标记的日期对应着早些时候例子中输出的九个日期。输出第一行的日期显示在第一次匹配中,输出的第二行的日期显示在第二次匹配中,输出的第三行的日期显示在第三次匹配中。
模式匹配中的数据是如何被处理的
MATCH_RECOGNIZE子句执行了这些步骤:
1.行模式的输入表根据PARTITION BY子句被分区。每个分区包含输入表的数据集,它们在分区列上有相同的值。
2.每个行模式分区根据ORDER BY子句被排序。
3.每个排序后的分区被搜索PATTERN的匹配。
4.模式匹配通过在最早的行查找匹配来操作,在行模式匹配区间中的考虑顺序是按照ORDER BY子句所指定的方式来排序的。
在一系列行中的模式匹配是一种渐进的处理,一行接一行地检查是否能和模式相符。在这种渐进处理的模型中,在整个模式被识别出来之前的任何步骤,你都只有部分匹配,而且你不知道未来加入的是什么行,也不知道那些未来的行会被映射到什么变量。
如果在最早的行没有找到匹配,搜索就会转移到分区中的下一行,查看是否从那行开始可以找到一个匹配。
5.在匹配找到之后,行模式匹配就会计算行模式中的度量列,这些就是MEASURES子句中定义的表达式。
6.如果使用的是第一个例子中所示的ONE ROW PER MATCH, 模式匹配就会为每个找到的匹配生成一行。如果你用的是ALL ROWS PER MATCH,每一个匹配到的行都会包括在模式匹配的输出中。
7.AFTER MATCH SKIP子句决定了在一个非空匹配被找到之后,行模式匹配要在区间中的何处重新开始。在前一例子中,行模式匹配重启于找到的匹配的最后一行(AFTER MATCH SKIP TO LAST UP)
模式匹配的特殊功能
这些功能是:
.规则表达式是系统用来搜索数据模式的一种稳健和由来已久的方式。Perl语言的规则达式功能被采纳为模式匹配规则的设计目标,而Oracle数据库12c的第1版,为模式匹配实现了这些规则的一个子集。
.Oracle的规则和典型的规则表达式之间的不同之处在于,行模式变量是按布尔条件来定义,而不是字符或字符的集合。
.虽然模式匹配使用规则表达式的记法来表达模式,实际上它的功能更加丰富,因为模式变量可以被定义为依赖于前面的行被映射到行模式变量的方式。DEFINE子句使得模式变量可以建立在其他模式变量之上。
.在行模式和度量的定义中可以使用子查询。
模式匹配中的基本主题
本章节将会讨论:
.模式匹配的基本例子
.模式匹配中的任务以及关键字
.模式匹配的语法
模式匹配的基本例子
本章节包含了一些模式匹配的基本例子。
例子20-2的第一行是为了改善输出格式,如果你用的是SQL*Plus的话。
例子20-2 简单的V形模式匹配,输出所有匹配行
column var_match format a4
SELECT *
FROM Ticker MATCH_RECOGNIZE (
PARTITION BY symbol
ORDER BY tstamp
MEASURES STRT.tstamp AS start_tstamp,
FINAL LAST(DOWN.tstamp) AS bottom_tstamp,
FINAL LAST(UP.tstamp) AS end_tstamp,
MATCH_NUMBER() AS match_num,
CLASSIFIER() AS var_match
ALL ROWS PER MATCH
AFTER MATCH SKIP TO LAST UP
PATTERN (STRT DOWN+ UP+)
DEFINE
DOWN AS DOWN.price < PREV(DOWN.price),
UP AS UP.price > PREV(UP.price)
) MR
ORDER BY MR.symbol, MR.match_num, MR.tstamp;
SYMBOL TSTAMP START_TST BOTTOM_TS END_TSTAM MATCH_NUM VAR_ PRICE
---------- --------- --------- --------- --------- ---------- ---- ----------
ACME 05-APR-11 05-APR-11 06-APR-11 10-APR-11 1 STRT 25
ACME 06-APR-11 05-APR-11 06-APR-11 10-APR-11 1 DOWN 12
ACME 07-APR-11 05-APR-11 06-APR-11 10-APR-11 1 UP 15
ACME 08-APR-11 05-APR-11 06-APR-11 10-APR-11 1 UP 20
ACME 09-APR-11 05-APR-11 06-APR-11 10-APR-11 1 UP 24
ACME 10-APR-11 05-APR-11 06-APR-11 10-APR-11 1 UP 25
ACME 10-APR-11 10-APR-11 12-APR-11 13-APR-11 2 STRT 25
ACME 11-APR-11 10-APR-11 12-APR-11 13-APR-11 2 DOWN 19
ACME 12-APR-11 10-APR-11 12-APR-11 13-APR-11 2 DOWN 15
ACME 13-APR-11 10-APR-11 12-APR-11 13-APR-11 2 UP 25
ACME 14-APR-11 14-APR-11 16-APR-11 18-APR-11 3 STRT 25
ACME 15-APR-11 14-APR-11 16-APR-11 18-APR-11 3 DOWN 14
ACME 16-APR-11 14-APR-11 16-APR-11 18-APR-11 3 DOWN 12
ACME 17-APR-11 14-APR-11 16-APR-11 18-APR-11 3 UP 14
ACME 18-APR-11 14-APR-11 16-APR-11 18-APR-11 3 UP 24
15 rows selected.
这个查询做了什么?它类似于例子 20-1 中的查询,只是MEASURES子句中的项目不同,以及修改为ALL ROWS PER MATCH, 以及查询结尾的ORDER BY的修改。在MEASURES子句中新增了这些:
.MATCH_NUMBER() AS match_num
因为这个例子为每次匹配给出了多行,你必须知道哪些行是哪次匹配的成员。MATCH_NUMBER为一个特定的匹配中的每一行赋予相同的数字。例如,在一个行模式匹配的第一次匹配中的所有行,其match_num的值都被赋值为1。注意在每一个行模式的分区中,这个匹配号又会从1开始。
.CLASSIFIER() AS var_match
为了得知哪一行映射到哪个变量,请使用CLASSIFIER函数。在这个例子中,有些行会映射到STRT变量,一些行映射到DOWN变量,其他的映射到UP变量。
.FINAL LAST()
通过在bottom_tstamp指定FINAL并使用LAST()函数,每次匹配中的每一行都显示同样的日期,即它的V形的底部。类似地,在end_tstamp度量应用FINAL LAST()使得每次匹配中的每一行显示相同的日期,即V形的结束。如果没加这个语法,显示的日期就是每行的滚动值。
其他两行中的修改:
.ALL ROWS PER MATCH —— 例子20-1 用ONE ROW PER MATCH为每次匹配给出了仅仅一行的汇总,这个例子则要求显示每次匹配中的每一行。
.最后一行的 ORDER BY —— 这是为了利用MATCH_NUM,因此每次匹配中的所有行都会在一起,并且按先后排序。
注意四月十号的行出现了两次,因为它在两个模式匹配中:它是第一次匹配的最后一天,也是第二次匹配的第一天。
例子 20-3 在一个变量上聚合的模式匹配
例子 20-3 突出了聚合函数在模式匹配查询中的使用。
SELECT *
FROM Ticker MATCH_RECOGNIZE (
PARTITION BY symbol
ORDER BY tstamp
MEASURES
MATCH_NUMBER() AS match_num,
CLASSIFIER() AS var_match,
FINAL COUNT(UP.tstamp) AS up_days,
FINAL COUNT(tstamp) AS total_days,
RUNNING COUNT(tstamp) AS cnt_days,
price - STRT.price AS price_dif
ALL ROWS PER MATCH
AFTER MATCH SKIP TO LAST UP
PATTERN (STRT DOWN+ UP+)
DEFINE
DOWN AS DOWN.price < PREV(DOWN.price),
UP AS UP.price > PREV(UP.price)
) MR
ORDER BY MR.symbol, MR.match_num, MR.tstamp;
SYMBOL TSTAMP MATCH_NUM VAR_ UP_DAYS TOTAL_DAYS CNT_DAYS PRICE_DIF PRICE
------ --------- --------- ---- ------- ---------- -------- --------- -----
ACME 05-APR-11 1 STRT 4 6 1 0 25
ACME 06-APR-11 1 DOWN 4 6 2 -13 12
ACME 07-APR-11 1 UP 4 6 3 -10 15
ACME 08-APR-11 1 UP 4 6 4 -5 20
ACME 09-APR-11 1 UP 4 6 5 -1 24
ACME 10-APR-11 1 UP 4 6 6 0 25
ACME 10-APR-11 2 STRT 1 4 1 0 25
ACME 11-APR-11 2 DOWN 1 4 2 -6 19
ACME 12-APR-11 2 DOWN 1 4 3 -10 15
ACME 13-APR-11 2 UP 1 4 4 0 25
ACME 14-APR-11 3 STRT 2 5 1 0 25
ACME 15-APR-11 3 DOWN 2 5 2 -11 14
ACME 16-APR-11 3 DOWN 2 5 3 -13 12
ACME 17-APR-11 3 UP 2 5 4 -11 14
ACME 18-APR-11 3 UP 2 5 5 -1 24
15 rows selected.
这个查询做了什么?它构建于例子20-2,增加了三个使用聚合函数COUNT()的度量。它还增加了一个度量以显示一个表达式中如何使用一个修饰列与未修饰列。
up_days度量(带有FINAL COUNT)显示了每个匹配中映射到UP模式变量的天数。你可以通过对图20-2中每次匹配的UP标签进行计数来检验。
total_days度量(同样带有FINAL COUNT)引入了未修饰列的使用。因为这个度量指定了FINAL count(tstamp),没有模式变量用于修饰tstamp列,它返回的是一个匹配中包含的所有行的计数。
cnt_days度量引入了RUNNING关键字。这个度量给出了一个滚动计数,它有助于区分一个匹配中的数据行。注意它同样没有用模式变量来修饰tstamp列,所以它适用于匹配中的所有行。在这个例子中你不需要明确使用RUNNING关键字,因为它是缺省的。更多细节请参见“Running 与 Final的语义和关键字对照”小节。
price_dif度量向我们显示了每天的股票价格与匹配的第一天价格相比的变动。在表达式"price - STRT.price),"中你看到了未修饰列"price,"和修饰列"STRT.price"被一起使用的例子。
例子20-4 演示了一个W形
例子20-4 W形匹配
SELECT *
FROM Ticker MATCH_RECOGNIZE (
PARTITION BY symbol
ORDER BY tstamp
MEASURES
MATCH_NUMBER() AS match_num,
CLASSIFIER() AS var_match,
STRT.tstamp AS start_tstamp,
FINAL LAST(UP.tstamp) AS end_tstamp
ALL ROWS PER MATCH
AFTER MATCH SKIP TO LAST UP
PATTERN (STRT DOWN+ UP+ DOWN+ UP+)
DEFINE
DOWN AS DOWN.price < PREV(DOWN.price),
UP AS UP.price > PREV(UP.price)
) MR
ORDER BY MR.symbol, MR.match_num, MR.tstamp;
SYMBOL TSTAMP MATCH_NUM VAR_ START_TST END_TSTAM PRICE
---------- --------- ---------- ---- --------- --------- ----------
ACME 05-APR-11 1 STRT 05-APR-11 13-APR-11 25
ACME 06-APR-11 1 DOWN 05-APR-11 13-APR-11 12
ACME 07-APR-11 1 UP 05-APR-11 13-APR-11 15
ACME 08-APR-11 1 UP 05-APR-11 13-APR-11 20
ACME 09-APR-11 1 UP 05-APR-11 13-APR-11 24
ACME 10-APR-11 1 UP 05-APR-11 13-APR-11 25
ACME 11-APR-11 1 DOWN 05-APR-11 13-APR-11 19
ACME 12-APR-11 1 DOWN 05-APR-11 13-APR-11 15
ACME 13-APR-11 1 UP 05-APR-11 13-APR-11 25
这个查询做了什么?它构建于例子20-1中所介绍的概念,并且在数据中寻找W形而不是V形。这个查询结果显示了一个W形。为了查找W形,定义PATTERN的规则表达式被修改为两次查找DOWN紧跟着UP的模式: PATTERN (STRT DOWN+ UP+ DOWN+ UP+)这个模式定义意味着仅当两个V形之间没有间隔时才匹配一个W形。例如,假设有一个平直间隔,价格不变,而这个间隔发生在两个V形之间,则此模式不会与数据匹配。为了演示返回数据,输出被设定为ALL ROWS PER MATCH。注意在MEASURES子句中的FINAL LAST(UP.tstamp)返回的是被映射到UP的最后一行的时间戳。
标签:
原文地址:http://www.cnblogs.com/miercler/p/5667987.html