分析函数又称为开窗函数,是一种特殊的内置函数。分析函数不会仅限于对每个group by的分组产生一个结果,它操作的是一个窗口(window),输入的行是排序和分组的,可以通过over()语句使用灵活的条件。impala的分析函数是从impala2.0.0开始添加的。分析函数经常被用于金融和科学领域,用来分析趋势、离群点以及大数据集的分桶分析。
当调用分析函数时,比如LEAD(),RANK()以及FIRST_VALUE,需要使用OVER从句。当调用聚合函数时使用了OVER从句,比如MAX(),COUNT()或者SUM(),将被视为分析函数。
语法
function(args) OVER([partition_by_clause] [order_by_clause [window_clause]])
partition_by_clause ::= PARTITION BY expr [, expr ...]
order_by_clause ::= ORDER BY expr [ASC | DESC] [NULLS FIRST
window_clause: See Window Clause
PARTITION BY从句
PARTITION BY 从句与GROUP BY从句类似,按照一列或者多列相同的值,将数据分成不同的组,这个逻辑上的组,称之为分区。但是,请注意以下限制,这些限制特别适用于涉及分区表的分析函数调用。当查询涉及到分析函数和分区表时,仅对分析函数调用的partition by子句中命名的列执行分区裁剪。比如,当一个分析函数查询带有WHERE year=2016的分区裁剪条件,只有在PARTITION BY 从句中指定year才可以裁剪掉其他的分区数据,比如 OVER (PARTITION BY year,other_columns
other_analytic_clauses)
分析函数作用的范围是一个分组内(分区)的数据,任意MAX(),SUM()或者ROW_NUMBER()等独立应用于每个分区。当省略PARTITION BY子句时,则会将分析操作应用于表中的所有行。
ORDER BY 从句
ORDER BY从句与一个查询中最外面的ORDER BY从句类似。它会对整个数据集或者PARTITION BY指定的分组数据进行排序。可以按一个或多个列进行排序,可以选择升序或降序以及空值是按排序顺序排在第一还是最后。因为ORDER BY从句仅仅对分析函数作用的数据进行排序,所以,如果使输出的结果是全排序的,可以在查询语句的最外层使用ORDER BY从句。当ORDER BY从句省略时,分析函数作用于PARTITION BY指定的分组的所有数据。当指定ORDER BY从句时,分析函数可以作用于分组的所有数据或者部分数据,这取决于WINDOW从句。在OVER从句内部使用的ORDER BY,与查询最外层的ORDER BY是有区别的,比如ORDER BY 1被解释为常量排序值(实际上是无操作),而不是按照第1列进行排序。
Window 从句
window从句只能ORDER BY 从句一起使用.。如果指定了 ORDER BY 从句,而省略了window从句,则默认的窗口(window)是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,表示从开始到当前行。
HBase 表注意事项
由于 HBase 表在单行查找做了优化,而不是全表扫描。所以不推荐在HBase表上使用分析函数,尽管查询是有效的,但是与查询HDFS上的数据相比,性能较差。
Parquet 表注意事项
在Parquet表上使用分析函数效率较高。
文本表注意事项
分析函数可以方便地与文本存储格式的表一起使用,但当数据量非常大时,优先使用 Parquet格式存储。在impala2.0.0添加的功能。
分析函数可以使用一个可选的window从句, 主要作用是让分析函数作用于某些行,而不是分组中的所有行。例如,可以通过指定前面和后面几行的数目,得到移动平均值。对于同一分组中的行,此子句可以产生不同的分析结果。 AVG(), COUNT(), FIRST_VALUE(), LAST_VALUE(), 和SUM()函数支持window子句。对于MAX()和 MIN(),window 从句只允许其实行为 UNBOUNDED PRECEDING,即第一行。
语法
ROWS BETWEEN [ { m | UNBOUNDED } PRECEDING | CURRENT ROW] [ AND [CURRENT ROW
| { UNBOUNDED | n } FOLLOWING] ]
RANGE BETWEEN [ {m | UNBOUNDED } PRECEDING | CURRENT ROW] [ AND [CURRENT ROW
| { UNBOUNDED | n } FOLLOWING] ]
目前, 对于RANGE从句,Impala仅支持以下几种组合:
• RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,为默认值,即当指定了ORDER BY从句,而省略了window从句 ,表示从开始到当前行。
• RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING,表示从当前行到最后一行
• RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,表示所有行
•n PRECEDING m FOLLOWING: 表示窗口的范围是:[(当前行的行数)- n, (当前行的行数)+m]
row.
关于window从句示意图,如下图所示:
例子:
--创建表
create table stock_ticker
(stock_symbol string,
closing_price decimal(8,2),
closing_date timestamp);
--插入数据
insert into stock_ticker values ("JDR",12.86,"2014-10-02 00:00:00"),
("JDR",12.89,"2014-10-03 00:00:00"),
("JDR",12.94,"2014-10-04 00:00:00"),
("JDR",12.55,"2014-10-05 00:00:00"),
("JDR",14.03,"2014-10-06 00:00:00"),
("JDR",14.75,"2014-10-07 00:00:00"),
("JDR",13.98,"2014-10-08 00:00:00");
如果只对分组的所有数据做统计,则可以不指定ORDER BY从句。比如:
--SQL1,统计分组数据的行数
select
stock_symbol,
closing_date,
closing_price,
count(*) over (partition by stock_symbol) cnt
from stock_ticker;
--SQL1结果输出
+--------------+---------------------+---------------+-----+
| stock_symbol | closing_date | closing_price | cnt |
+--------------+---------------------+---------------+-----+
| JDR | 2014-10-02 00:00:00 | 12.86 | 7 |
| JDR | 2014-10-03 00:00:00 | 12.89 | 7 |
| JDR | 2014-10-04 00:00:00 | 12.94 | 7 |
| JDR | 2014-10-05 00:00:00 | 12.55 | 7 |
| JDR | 2014-10-06 00:00:00 | 14.03 | 7 |
| JDR | 2014-10-07 00:00:00 | 14.75 | 7 |
| JDR | 2014-10-08 00:00:00 | 13.98 | 7 |
+--------------+---------------------+---------------+-----+
--SQL2,对分组的数据按某个字段求和
select
stock_symbol,
closing_date,
closing_price,
sum(closing_price ) over (partition by stock_symbol) sums
from stock_ticker;
--SQL2结果输出
+--------------+---------------------+---------------+-------+
| stock_symbol | closing_date | closing_price | sums |
+--------------+---------------------+---------------+-------+
| JDR | 2014-10-02 00:00:00 | 12.86 | 94.00 |
| JDR | 2014-10-03 00:00:00 | 12.89 | 94.00 |
| JDR | 2014-10-04 00:00:00 | 12.94 | 94.00 |
| JDR | 2014-10-05 00:00:00 | 12.55 | 94.00 |
| JDR | 2014-10-06 00:00:00 | 14.03 | 94.00 |
| JDR | 2014-10-07 00:00:00 | 14.75 | 94.00 |
| JDR | 2014-10-08 00:00:00 | 13.98 | 94.00 |
+--------------+---------------------+---------------+-------+
--SQL3,查找分组内的第一个值
select
stock_symbol,
closing_date,
closing_price,
first_value(closing_price) over (partition by stock_symbol) firstvalue
from stock_ticker;
--SQL3结果输出
+--------------+---------------------+---------------+------------+
| stock_symbol | closing_date | closing_price | firstvalue |
+--------------+---------------------+---------------+------------+
| JDR | 2014-10-02 00:00:00 | 12.86 | 12.86 |
| JDR | 2014-10-03 00:00:00 | 12.89 | 12.86 |
| JDR | 2014-10-04 00:00:00 | 12.94 | 12.86 |
| JDR | 2014-10-05 00:00:00 | 12.55 | 12.86 |
| JDR | 2014-10-06 00:00:00 | 14.03 | 12.86 |
| JDR | 2014-10-07 00:00:00 | 14.75 | 12.86 |
| JDR | 2014-10-08 00:00:00 | 13.98 | 12.86 |
+--------------+---------------------+---------------+------------+
--SQL4,查找分组内的最后一个值
select
stock_symbol,
closing_date,
closing_price,
last_value(closing_price) over (partition by stock_symbol) as lastvalue
from stock_ticker;
SQL4结果输出
+--------------+---------------------+---------------+-----------+
| stock_symbol | closing_date | closing_price | lastvalue |
+--------------+---------------------+---------------+-----------+
| JDR | 2014-10-02 00:00:00 | 12.86 | 13.98 |
| JDR | 2014-10-03 00:00:00 | 12.89 | 13.98 |
| JDR | 2014-10-04 00:00:00 | 12.94 | 13.98 |
| JDR | 2014-10-05 00:00:00 | 12.55 | 13.98 |
| JDR | 2014-10-06 00:00:00 | 14.03 | 13.98 |
| JDR | 2014-10-07 00:00:00 | 14.75 | 13.98 |
| JDR | 2014-10-08 00:00:00 | 13.98 | 13.98 |
+--------------+---------------------+---------------+-----------+
--SQL5,查找分组内的某个字段的最大值
select
stock_symbol,
closing_date,
closing_price,
max(closing_price) over (partition by stock_symbol) as maxvalue
from stock_ticker;
SQL5结果输出
+--------------+---------------------+---------------+----------+
| stock_symbol | closing_date | closing_price | maxvalue |
+--------------+---------------------+---------------+----------+
| JDR | 2014-10-02 00:00:00 | 12.86 | 14.75 |
| JDR | 2014-10-03 00:00:00 | 12.89 | 14.75 |
| JDR | 2014-10-04 00:00:00 | 12.94 | 14.75 |
| JDR | 2014-10-05 00:00:00 | 12.55 | 14.75 |
| JDR | 2014-10-06 00:00:00 | 14.03 | 14.75 |
| JDR | 2014-10-07 00:00:00 | 14.75 | 14.75 |
| JDR | 2014-10-08 00:00:00 | 13.98 | 14.75 |
+--------------+---------------------+---------------+----------+
下面的例子是求移动平均值
select
stock_symbol,
closing_date,
closing_price,
avg(closing_price) over (partition by stock_symbol order by closing_date
rows between 1 preceding and 1 following) as moving_average
from stock_ticker;
-- rows between 1 preceding and 1 following的含义是:
窗口的范围为三行数据,即[当前行 -1 ,当前行 + 1]。
对于第一行而言,不存在前一行数据,所以只统计[第一行,第一行 + 1]的平均值。
对于最后一行而言,不存在后一行数据,所以只统计[最后一行 -1 ,最后一行]的平均值
--结果输出
+--------------+---------------------+---------------+----------------+
| stock_symbol | closing_date | closing_price | moving_average |
+--------------+---------------------+---------------+----------------+
| JDR | 2014-10-02 00:00:00 | 12.86 | 12.87 |
| JDR | 2014-10-03 00:00:00 | 12.89 | 12.89 |
| JDR | 2014-10-04 00:00:00 | 12.94 | 12.79 |
| JDR | 2014-10-05 00:00:00 | 12.55 | 13.17 |
| JDR | 2014-10-06 00:00:00 | 14.03 | 13.77 |
| JDR | 2014-10-07 00:00:00 | 14.75 | 14.25 |
| JDR | 2014-10-08 00:00:00 | 13.98 | 14.36 |
+--------------+---------------------+---------------+----------------+
具体分析函数参见我的另一篇博客:https://blog.csdn.net/jmx_bigdata/article/details/8805963