Hive函数


一、Hive函数分类

1. Hive函数划分介绍

Hive函数划分介绍:
    函数介绍(大白话):
        函数就是开发者定义好的规则, 我们只要调用指定的函数, 传入对应的参数, 就能获取对应的结果, 我们无需关心函数内部是如何执行的, 只要会调用即可.
        可以把函数理解为是人们研发的机器, 不同的机器所需的原材料(参数)是不一样的, 传入不同的原材料(参数), 就会获取不同的结果.
    概述:
        最初Hive的函数分为 内置函数 和 用户自定义函数两大类, 而用户自定义函数又分为 UDF, UDAF, UDTF三种.
        后来人们(程序员)发现用 UDF, UDAF, UDTF来划分hive函数太方便精准了, 于是在Hive2.X版本中提出了1个概念: 函数标准扩大化.
        即: 从Hive2.4X开始, Hive中的函数就只有: UDF, UDAF, UDTF三种了.
    分类:
        UDF:    全称 user defined functions, 也叫: 普通函数.
            核心: 一进一出.  即: 给函数10行数据, 处理之后, 返回10.
            例如: abs(), round()...
        UDAF:   全称 user defined Aggregate functions, 也叫: 聚合函数.
            核心: 多进一出, 即: 给函数10行数据, 处理之后, 返回1.
            例如: count(), max(), sum(), avg(), min()
        UDTF:   全称 user defined Table Generating functions, 也叫: 表生成函数.
            核心: 一进多出, 即: 给函数1, 它返回多条.
            例如: explode() 爆炸函数(炸裂函数)

2. 案例–Hive函数划分

-- ------------------------ 案例1: hive函数划分 ------------------------
use day08;
--- 1. UDF函数演示, 一进一出.
select abs(-10);    -- 10
select round(10.123, 2);    -- 10.12

-- 2. UDAF函数演示, 多进一出.
select count(sid) from student;     -- 22条

-- 3. UDTF函数演示, 一进多出.
select array('aa', 'bb', 'cc');     -- 演示数据源, 即: 一行数据.  这个不是 UDTF函数.
select explode(array('aa', 'bb', 'cc'));    -- 一进多出, 这个才是UDTF函数.

-- 4. 扩展, 如果某个函数不会用, 可以查看下它的说明文档, 具体如下.
-- 官网网址: https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF#LanguageManualUDF-MathematicalFunctions

-- 5. 函数基础语法.
show functions ;        -- 查看hive所有的函数, hive中符号也是函数, 只不过是: 函数名为符号而已.
describe function abs;              -- 查看函数的说明文档(简单信息)
describe function extended abs;     -- 查看函数的说明文档(详细信息).

二、Hive函数-字符串相关函数

1. 字符串相关函数

-- ------------------------ 案例2: Hive函数-字符串相关函数 ------------------------
-- 演示: split(参1, 参2)函数, 参1: 要切割的字符串, 参2: 切割符(支持正则)
select split('a1b2c3d', '1');           -- ["a","b2c3d"], 按照数字1对字符串切割
select split('a1b2c3d', '[0-9]');       -- ["a","b","c","d"], 按照数字对字符串切割
select split('a1b2c3d', '\\d+');      -- ["a","b","c","d"], 按照数字对字符串切割

-- 演示: concat()函数, 默认拼接符是: '', 可以拼接任意类型.
select concat('-', 'aa', 'bb', 11, 'false'); -- -aabb11false
-- 演示: concat_ws()函数, 可以指定拼接符. 参1: 拼接符号, 参2.... 要拼接的内容.
select concat_ws('-', 'aa', 'bb', 'cc', array('x', 'y', 'z')); -- 只能拼字符串, 或者字符串数组      aa-bb-cc-x-y-z
-- 演示: trim()函数, 移除两端空格.
select trim('    aa   bb     ');           -- 'aa   bb'
-- 演示: substr()函数, 参1: 要操作的字符串, 参2:从几开始截取,默认从1开始计数.  参3: 截取几个.
select substr('hello world hive', 7);    -- world hive, 从7开始, 截取到结尾.
select substr('hello world hive', 7, 5);    -- world hive, 从7开始, 截取5个
select substr('hello world hive', -4);    -- hive, 从-4索引(从右往左数, 从-1开始)
select substr('hello world hive', 13);    -- hive, 从13索引开始.

select substr('2024-01-14 10:16:19', 1, 10);       -- 年月日
select substr('2024-01-14 10:16:19', 6, 2);        -- 月
select substr('2024-01-14 10:16:19',9, 2);         -- 日

-- 演示: substring()函数, 效果同上.
select substring('2024-01-14 10:16:19', 1, 10);       -- 年月日

-- 演示: lower()函数,
select lower('ABC123');     -- abc123
-- 演示: upper()函数,
select upper('abc123');     -- ABC123

-- 演示: regexp_replace()函数, 正则替换, 参1: 要操作的字符串, 参2: 正则表达式, 参3: 替换后的内容.
select regexp_replace('aa11bb22cc33dd', '\\d+', '#');

-- 演示: parse_url()函数, 从URL地址中解析出指定的内容.  参1: url地址, 参2: 要解析出的内容, 例如: HOST, PATH, QUERY
--                          域名          文件路径     参数
select parse_url('https://www.itcast.cn/index.html?username=root&password=123456', 'HOST'); -- www.itcast.cn
select parse_url('https://www.itcast.cn/index.html?username=root&password=123456', 'PATH'); -- /index.html
select parse_url('https://www.itcast.cn/index.html?username=root&password=123456', 'QUERY'); -- username=root&password=123456'
select parse_url('https://www.itcast.cn/index.html?username=root&password=123456', 'QUERY', 'username'); -- root

2. json字符串

-- 演示: get_json_object(), 处理json字符串的.   参1: json字符串, 参2: 从json中解析出对应内容, 可以用 $代表整个json字符串 或者 json数组.

json字符串介绍:
    概述:
        它是一种特殊规则的字符串, 由键值对组成. 只要用双引号包裹.
    格式:
        {"键":"值", "键":"值"}
    作用:
        一般是前后端传输数据的, 跨域传输, 例如: 前端把数据封装好 然后传入到 后台代码中.
    细节:
        json字符串也有数组形式:  [{"键":"值", "键":"值"}, {"键":"值", "键":"值"}...]

3. 案例–json字符串

select get_json_object('{"name":"乔峰", "age":31}', '$.name');    -- 乔峰
select get_json_object("{'name':'乔峰', 'age':31}", '$.name');    -- null, json字符串用双引号包裹.
-- json数组
select get_json_object('[{"name":"乔峰", "age":31}, {"name":"虚竹", "age":29}]', '$.[1].name');    -- 虚竹, 索引从0开始.

三、Hive函数–日期相关函数

-- ------------------------ 案例3: Hive函数-字符串相关函数 ------------------------
-- 1. 获取当前的时间戳.
select `current_timestamp`();       -- 2024-01-14 10:57:23.763000000

-- 2. 获取当前的时间.
select `current_date`();    -- 2024-01-14

-- 3. 获取当前的秒值, 即: 从时间原点(1970年1月1日 00:00:00) 至 当前时间的秒值
select unix_timestamp();                        -- 1705201180
select unix_timestamp('1970-01-01 00:00:01');   -- 1, 获取时间原点 ~ 指定时间的秒值.

-- 4. 把秒值 转成对应的 时间.
select from_unixtime(1705201180);       -- 2024-01-14 02:59:40

-- 5. 根据时间戳, 获取指定的日期: 年-月-日
select to_date(`current_timestamp`());

-- 6. 计算下你活了多少天.
--         当前时间的秒值  -   你出生那天的秒值
select unix_timestamp() - unix_timestamp('2002-08-07 00:00:00');                    -- 676523149秒
select (unix_timestamp() - unix_timestamp('2002-08-07 00:00:00')) / 3600 / 24;      -- 7830.129537037036....

-- 7. 计算两个日期的差值.
select datediff('2024-01-10 00:00:00', '2024-01-05 00:00:00');  --  结果: 前 - 后, 5

-- 8. 从日期中获取对应的内容.
select year('2024-01-10 13:14:21');
select month('2024-01-10 13:14:21');
select day('2024-01-10 13:14:21');
select hour('2024-01-10 13:14:21');
select minute('2024-01-10 13:14:21');
select second('2024-01-10 13:14:21');
select quarter('2024-01-10 13:14:21');          -- 掌握, 季度.

-- 9. 偏移指定的时间.
select date_add('2024-01-10 13:14:21', 2);    -- 往后推2天, 2024-01-12
select date_sub('2024-01-10 13:14:21', -2);   -- 往后推2天, 2024-01-12,      subtract: 减法

select date_add('2024-01-10 13:14:21', -2);  -- 往前推2天, 2024-01-08
select date_sub('2024-01-10 13:14:21', 2);   -- 往前推2天, 2024-01-08,      subtract: 减法

四、Hive函数–数字相关

-- ------------------------ 案例4: Hive函数-数字相关函数 ------------------------
-- 1. 求绝对值.
select abs(-10);    -- 10

-- 2. 四舍五入. round
select round(10.312, 2);    -- 10.31
select round(10.315, 2);    -- 10.32

-- 3. 随机数
select rand();      -- 0.0 ~ 1.0之间, 包左不包右.
-- 新需求: 获取1个 1 ~ 100之间的随机数
select int(rand() * 100) + 1;   -- rand() * 100 获取 0.0 ~ 100.0 之间, 包左不包右的数字.

-- 4. ceil() 天花板数, 比这个数字大的所有数字中, 最小的那个整数.
select ceil(10.3);  -- 11
select ceil(10.0);  -- 10

-- 5. floor() 地板数.
select floor(10.3);  -- 10
select floor(10.0);  -- 10

-- 6. pow(a, b), 计平方值, a的b次幂
select pow(3, 2);   -- 9

-- 7. 面试题: 四舍五入的原理是什么?      +0.5, 然后求地板数.
select `floor`(10.3 + 0.5);     -- 10
select `floor`(10.4 + 0.5);     -- 10
select `floor`(10.5 + 0.5);     -- 11

五、Hive函数–转换相关

-- ------------------------ 案例5: Hive函数-判断, 转换相关函数 ------------------------
-- 1. if()函数, 做判断的, 条件成立返回值1, 条件不成立返回值2.  参1: 关系表达式, 参2: 值1, 参3: 值2
select if(5 > 3, '成立', '不成立');

-- 2. isnull() 判断是否为空.
select isnull('');      -- false
select isnull(null);    -- true

-- 3. isnotnull() 判断是否不为空.
select isnotnull('');      -- true
select isnotnull(null);    -- false

-- 4. nvl() 空值转换, 判断参1是否为空, 不为空就返回其值, 为空就返回值2.
select nvl('乔峰', '虚竹');     -- 乔峰
select nvl(null, '虚竹');      -- 虚竹

-- 5. coalesce(值1, 值2...)   获取众多参数中的第1个非空值.
select coalesce(null, null, null);              -- null
select coalesce(null, null, 1, 2, 3);           -- 1
select coalesce(null, 'y', null, 'a', '10');    -- y
select coalesce('a', '10', 'b', '20');          -- 'a'

-- 6. case when
select
    case
        when 5 > 3 then '大于'
        when 5 < 3 then '小于'
        else '等于'
    end as col1;

-- case when语法糖, 如果都是等于判断, 且是同一列, 可以简写为如下写法.
select
    case 1      -- 应该是: case 列名
        when 1 then '星期一'
        when 2 then '星期二'
        when 3 then '星期三'
        else '没有这样的时间'
    end as week;

-- 7. cast()函数, 类型转换.
select cast(10.3 as int);   -- 10
select cast(10.6 as int);   -- 10
select cast(10 as string);  -- '10'
select cast('123' as int);  -- 123, 字符串 -> 数字
select cast('123' as boolean);  -- true, 非空 '', 非0, 非null
select cast('' as boolean);  -- false

六、Hive函数–脱敏,加密相关

-- ------------------------ 案例6: Hive函数-加密, 脱敏相关函数 ------------------------
-- 1. 获取哈希值.
select hash('abc');     -- 96354

-- 2. 加密相关函数.
select md5('Aa1');    -- 698d51a19d8a121ce581499d7b701668
select sha1('Aa1');   -- f55cd2f8d3186c960aa86782452979118760e96d
select sha2('Aa1', 512);   -- 2907b40d2cb5527fe522b1c97f90dbf49423dd0cfd411f5d91528bc38c0c2ee9515d981816149d138ffa9d2ac80ef1d0435dc0213bdb85b2f85f446c4a035f3f
describe function extended sha2;
select crc32('Aa1');       -- 2736874553

-- 3. 脱敏相关.
select mask('xyzABC123');       --结果为: xxxXXXnnn,  默认顺序是: 大写字母X, 小写字母x, 数字n
-- 细节: 我想指定小写字母用 小 来替换.   参1: 要脱敏的字符串.  参2: 大写字母替换符, 参3: 小写字母替换符, 参4: 数字替换符.
select mask('xyzABC123', 'X', '小');  -- xxx小小小nnn

-- 4. 只脱敏前几个字符.
select mask_first_n('ABCabc123', 4);        -- XXXxbc123 只处理前 4 个字符.
select mask_show_first_n('ABCabc123', 4);   -- ABCaxxnnn 除了前 4 个字符, 其它都处理.
-- 自学: mask_last_n(),  mask_show_last_n()

-- 5. 需求: 把13112345678 => 131****5678
select concat(substr('13112345678', 1, 3), mask(substr('13112345678', 4, 4), 'X', 'x', '*'), substr('13112345678', -4));

-- 6. sort_array()函数, 对数组进行排序, 属于: 集合函数.
 select sort_array(array(11, 33, 22, 55));      -- [11,22,33,55]

七、Hive函数–其它函数

-- ------------------------ 案例7: Hive函数-其它函数 ------------------------
-- 1. 查看当前的用户
select current_user();      -- root

-- 2. 查看当前在使用的数据库.
select current_database();

-- 3. 查看当前的hive版本.
select version();       -- 3.1.2 r8190d2be7b7165effa62bd21b7d60ef81fb0e4af

八、Hive高阶函数–列转行(初体验)

-- ------------------------ 案例8: Hive高阶函数-行列转换 之初体验(列转行) ------------------------
-- 1. 铺垫知识. 列转行需要用到 explode()函数, 爆炸函数, 它属于UDTF(表生成函数), 即: 一进多出. 只能处理 数组或者字典.
select `array`(11, 22, 33);
select explode(array(11, 22, 33));      -- 一进多出,  列转行
select explode(map(11, 22, 33));        -- 报错, map是键值对类型, 元素个数必须成对出现.
select explode(map(11, 22, 33, 44));        -- 报错, map是键值对类型, 元素个数必须成对出现.

-- 2. 建表, NBA历年夺冠球队信息.
-- 数据格式为: Chicago Bulls,1991|1992|1993|1996|1997|1998
create table the_nba_championyear(
    team_name string comment '队名',
    champion_year array<string> comment '夺冠年份'
) row format delimited
    fields terminated by ','                -- 切割后, 数据为: "Chicago Bulls", "1991|1992|1993|1996|1997|1998"
    collection items terminated by '|';     -- 切割后, 数据为: "Chicago Bulls", ["1991", "1992", "1993", "1996", "1997", "1998"]

-- 3. 上传源文件, 然后查看数据.
select * from the_nba_championyear;

/*
至此, 数据已经加载成功了, 但是不符合我们的格式, 我要的数据格式为:
    team_name           champion_year
    Chicago Bulls         1991
    Chicago Bulls         1992
    Chicago Bulls         1993
    Chicago Bulls         1996
    Chicago Bulls         1997
    Chicago Bulls         1998
    San Antonio Spurs     1999
    San Antonio Spurs     2003
    ......
*/

-- 4. 思路很简单, 把 champion_year字段用 explode()爆炸函数, 炸开即可.
select explode(champion_year) as champion_year from the_nba_championyear;

-- 5. 走到这里, 就剩下最后1步了, 在上述查询基础上, 加入: 队名即可, 写法如下.
select team_name, explode(champion_year) as champion_year from the_nba_championyear;    -- 想法OK, 但是报错.

-- 6. 思考: 为啥报错?
-- 产生原因: 炸裂后的数据, 无法直接和原表查询, 因为对应的关联信息不精准(炸裂后的每一行, 具体对应的是炸裂前的哪一行数据, 对不上)
-- 解决方案: 通过侧视图(lateral view)实现, 它是Hive提供的一种专门用来记录 炸裂前后, 数据对应关系的函数.
-- 侧视图格式: lateral view 炸裂函数 侧视图的名字 as 炸裂后数据的列名
select a.team_name, b.champion_year from the_nba_championyear a
lateral view explode(champion_year) b as champion_year;

九、Hive高阶函数–行转列

1. 行列转换–行转列

行转列涉及到的函数:
    采集函数:
        collect_list()     采集, 不去重, 有序(不是排序, 而是元素的存取顺序).
        collect_set()      采集, 去重, 无序.
    拼接函数:
        concat()           默认拼接符 ''
        concat_ws()        可以指定拼接符.

2. 案例–行转列

-- ------------------------ 案例9: Hive高阶函数-行列转换 之 行转列 ------------------------
-- 1. 建表.
create table row2col2(
     col1 string,
     col2 string,
     col3 int
)row format delimited fields terminated by '\t';

-- 2. 上传源文件.
-- 3. 查看表数据.
select * from row2col2;

-- 4. HiveSQL语句--完成 行转列 代码实现.
select
    col1, col2,
    /*
     oncat_ws('-', collect_list(cast(col3 as string))) as col3 代码解释:
        cast(col3 as string):               把 col3 列的数据, 转成 string类型, 就能通过 concat_ws()拼接了.
        collect_list(cast(col3 as string)): 根据分组条件, 采集每组所有的信息, 有序, 可重复.
        collect_set(cast(col3 as string)): 根据分组条件, 采集每组所有的信息, 无序, 唯一.
        oncat_ws('-', collect_list(cast(col3 as string))):      用'-'作为连接符, 把上述采集到的数据, 拼接到一起.
     */
    concat_ws('-', collect_list(cast(col3 as string))) as col3,
    concat_ws('-', collect_set(cast(col3 as string))) as col4
from
    row2col2
group by
    col1, col2;

十、Hive高阶函数–列转行(面使用)

1. 注意点

explode()只能处理 数组array, 映射map数据. 可以通过split()函数把字符串转成 array数组.

2. 案例–列转行

-- ------------------------ 案例10: Hive高阶函数-行列转换 之 列转行 ------------------------
-- 1. 建表.
create table col2row2(
    col1 string,
    col2 string,
    col3 string
)row format delimited fields terminated by '\t';

-- 2. 上传源文件.
-- 3. 查看表数据.
select * from col2row2;

-- 4. HiveSQL语句--完成 列转行 代码实现.
select a.col1, a.col2, b.col3 from col2row2 a
lateral view explode(split(col3, ',')) b as col3;

-- 细节: explode()只能处理 数组array, 映射map数据. 可以通过split()函数把字符串转成 array数组.
select explode(split('1,2,3', ','));

十一、Hive高阶函数–处理json字符串

1. json字符串介绍

json字符串介绍:
    概述:
        它是一种特殊规则的字符串, 由键值对组成. 只要用双引号包裹.
    格式:
        {"键":"值", "键":"值"}
    作用:
        一般是前后端传输数据的, 跨域传输, 例如: 前端把数据封装好 然后传入到 后台代码中.
    细节:
        json字符串也有数组形式:  [{"键":"值", "键":"值"}, {"键":"值", "键":"值"}...]
    Hive中处理Json字符串主要有两种思路:
        思路1: 以普通字符串的形式存储json字符串, 然后解析.          简单说: 先存储, 再解析.
            方式1: get_json_object()      逐个解析.
            方式2: json_tuple()           批量解析.
        思路2: 在加载(建表)json文件的时候, 直接解析json字符串.      简单说: 存储时, 直接解.

2. 案例–处理json字符串

-- ------------------------ 案例11: Hive高阶函数-处理json字符串 ------------------------
-- 演示 思路1: 以普通字符串的形式存储json字符串, 然后解析.          
-- 简单说: 先存储, 再解析.
-- 1. 建表.
create table t_json1(
    json string
);
-- 2. 上传源文件.
-- 3. 查看源数据.
select * from t_json1;
-- 4. 解析json字符串.
-- 4.1 方式1: get_json_object()      逐个解析.
select
    get_json_object(json, '$.device') as device,            -- 设备名
    get_json_object(json, '$.deviceType') as deviceType,     -- 设备类型
    get_json_object(json, '$.signal') as signal,    -- 信号强度
    get_json_object(json, '$.time') as `time`    -- 数据采集时间
from
    t_json1;

-- 4.2 方式2: json_tuple()           批量解析.
select
    -- 格式: json_tuple(json字符串, '键名1', '键名2', '键名3'...) as (别名1, 别名2, 别名3...)
    json_tuple(json, 'device', 'deviceType', 'signal', 'time') as (device, deviceType, signal, `time`)
from t_json1;

-- 演示 思路2: 在加载(建表)json文件的时候, 直接解析json字符串.      简单说: 存储时, 直接解.
drop table t_json2;
-- 1. 建表, 直接解析 json字符串.
create table t_json2(
    device string,
    deviceType string,
    signal string,
    `time` string
)
-- delimited方式默认用的是LazySimpleSerDe类, 实现不了我们的需求, 所以我们用Hive专门处理Json字符串SerDe类, 即: JsonSerDe类
row format SerDe 'org.apache.hive.hcatalog.data.JsonSerDe';

-- 2. 上传源文件.
-- 3. 查看源数据(看到的就是 解析好的内容)
select * from t_json2;

十二、Hive高阶函数–窗口函数

1. 窗口函数

窗口函数解释:
    概述:
        窗口函数相当于给表新增一列, 至于新增的内容是什么, 取决于窗口函数结合什么函数一起使用.
        窗口函数指的是 over()函数, 它可以限定操作数据的范围, 进行局部 或者 全局计算等...
    格式:
        可以结合窗口函数一起用的函数 over(partition by 分组字段 order by 排序字段 asc | desc rows between 起始行 and 结束行)
    可以结合窗口函数一起用的函数 分为3类:
        1. 聚合函数.
            count(), sum(), max(), min(), avg()
        2. 排序函数.
            row_number(), rank(), dense_rank(), ntile(n)
        3. 其它函数.
            lag(), lead(), first_value(), last_value()
    细节:
        1. 窗口函数相当于给表新增一列, 至于新增的内容是什么, 取决于窗口函数结合什么函数一起使用.
        2. 如果不写partition by, 默认是全局统计, 如果写了, 则只统计组内的数据(局部统计).
        3. 如果不写order by, 默认是统计组内所有数据, 如果写了, 则统计组内第一行至当前行的数据.
        4. 通过rows between可以限定操作数据的范围, 常用关键字如下:
            unbounded preceding         起始行(第一行)
            n preceding                 向上n行
            current row                 当前行
            n following                 向下n行
            unbounded following         结束行(最后1)
        5. ntile(n)是几分之几的意思, 表示把数据分成几份, 优先参考最小分区.
           例如: 7条数据, 分成3, 则是:   1,1,1      2,2     3,3
        6. 常用的 可以结合窗口函数一起用的函数 主要有: count(), sum(), row_number(), rank(), lag()

2. 案例–窗口函数

-- ------------------------ 案例12: Hive高阶函数-窗口函数 ------------------------

--  ----------------------- 以下是窗口函数, 准备动作 -----------------------------------------
--  切换数据库.
create database day09;
use day09;
show tables ;

--  员工表
CREATE TABLE employee (
    id     int,         --  编号
    name   string, --  姓名
    deg    string, --  职位
    salary int,         --  工资
    dept   string  --  部门
) row format delimited fields terminated by ',';

--  网站点击量表
create table website_pv_info
(
    cookieid   string, --  用户id
    createtime string, --  访问时间
    pv         int          --  页面浏览量
) row format delimited fields terminated by ',';


--  网站访问记录表
create table website_url_info
(
    cookieid   string, --  用户id
    createtime string, --  访问时间
    url        string  --  访问的url页面
) row format delimited fields terminated by ',';

--  ----------------------- 以下是窗口函数案例 初体验-------------------------------------
--  案例1: 窗口函数初体验, 需求: 统计所有员工的工资.
--  不使用窗口函数, 直接用传统的 聚合函数.
select sum(salary) from employee;

--  需求: 统计所有员工的工资, 并将其展示在每个员工数据的最后.
select *, sum(salary) over() from employee;

3. 窗口函数–结合聚合函数

--  ------------------------------------- 以下是窗口函数案例 窗口 + 聚合 --------------------------------
--  补充概述: PV: Page View 页面浏览量, UV: user view, 用户访问数 IP: ip(独立访客)访问数
--  例如: 我通过我电脑的谷歌浏览器访问了京东的10个页面, 通过Edge浏览器访问了京东的7个页面, 请问: PV, UV, IP分别是多少?
--  答案: IP: 1个, UV: 2个, PV: 17个
select * from website_pv_info;

--  案例2: 演示 窗口函数 + 聚合函数一起使用.
--  需求:求出网站总的pv数 所有用户所有访问加起来
--  如果over()里边什么都不写, 默认操作的是: 表中 该列所有的数据.
select *, sum(pv) over() as total_pv from website_pv_info;

--  需求: 求出每个用户总pv数
--  方式1: sum() + group by 一起使用.
select cookieid, sum(pv) as total_pv from website_pv_info group by cookieid;

--  方式2: 聚合函数 + 窗口函数一起使用.
--  细节: 如果写了partition by(表示分组): 则默认操作 组内所有的数据.
select *, sum(pv) over(partition by cookieid) as total_pv from website_pv_info;

--  细节: 如果写了order by(表示排序):     则默认操作 组内第一行 至 当前行的数据.
select *, sum(pv) over(partition by cookieid order by createtime) as total_pv from website_pv_info;

--  上述的代码, 等价于如下的内容:
select *,                                                                 --   第1行             至    当前行
       sum(pv) over (partition by cookieid order by createtime rows between unbounded preceding and current row ) as total_pv
from
    website_pv_info;

--  需求: 统计每个cookieID的pv(访问量), 只统计: 当前行及 向前3行 向后1行
select *,                                                                 --   向前3行    至    向后1行
       sum(pv) over (partition by cookieid order by createtime rows between 3 preceding and 1 following ) as total_pv
from
    website_pv_info;

4. 窗口函数–结合排序函数

--  ------------------------------------- 以下是窗口函数案例 窗口 + 排序 --------------------------------
--  需求: 根据点击量(pv)做排名, 组内排名.
--  这里的排序函数指的是: row_number(), rank(), dense_rank(), 它们都可以做排名, 不同的是, 对相同值的处理结果.
--  例如: 数据是100, 90, 90, 60, 则: row_number是: 1, 2, 3, 4,  rank: 1, 2, 2, 4,  dense_rank: 1, 2, 2, 3
select
    *,
    row_number() over (partition by cookieid order by pv desc ) as rn,      -- 根据用户id分组, 根据点击量, 降序排列.
    rank() over (partition by cookieid order by pv desc ) as rk,
    dense_rank() over (partition by cookieid order by pv desc ) as dr
from website_pv_info;
--  需求: 根据cookieID进行分组, 获取每组点击量最高的前4名数据, 这个就是经典的案例: 分组求TopN
--  Step1: 根据cookieID进行分组, 根据点击量进行排名.
select
    *,
    dense_rank() over (partition by cookieid order by pv desc ) as dr
from website_pv_info
where dr <= 4;          -- 报错, 思路没有问题, 但是: where只能筛选表中已经有的列(数据)
--  细节: where只能筛选表中已经有的列(数据)
--  Step2: 把上述的查询结果当做一张表, 然后从中获取我们要的数据即可.
with t1 as (
    select
        *,
        dense_rank() over (partition by cookieid order by pv desc ) as dr
    from website_pv_info
)
select * from t1 where dr <= 4;

--  ntile(数字,表示分成几份)  采用均分策略, 每份之间的差值不超过1, 优先参考最小的那个部分, 即: 7分成3份, 则是: 3, 2, 2
select
    *,
    ntile(3) over (partition by cookieid order by pv desc ) as nt      -- 根据用户id分组, 根据点击量, 降序排列. 分成3份
from website_pv_info;

-- 需求: 按照cookieid分组, 按照点击量降序排列, 只要每组前三分之一的数据.
with t1 as (
    select
        *,
        ntile(3) over (partition by cookieid order by pv desc ) as nt      -- 根据用户id分组, 根据点击量, 降序排列. 分成3份
    from website_pv_info
)
select * from t1 where nt = 1;

5. 窗口函数–结合其它函数

--  ---------------------- 以下是窗口函数案例 窗口 + 其它 --------------------------------
use day09;
--  1. LAG 用于统计窗口内往上第n行值
--  需求: 显示用户上一次的访问时间.
select
    *,
    -- 获取上1个createtime列的值,  根据cookieid分组   根据createtime升序排列
    lag(createtime) over(partition by cookieid order by createtime) lag_time
from
    website_url_info;

--  根据cookieID分组, createtime升序排序, 获取当前行 向上2行的createtime列的值, 找不到就用默认值(夯哥)填充.
select
    *,
    -- 获取createtime列向上第2个值, 如果没有就用 夯哥 作为默认值进行填充.
    -- 参1: 要操作的列,   参2: 向上第几个.  参3: 默认值.
    lag(createtime, 2, '夯哥') over(partition by cookieid order by createtime) lag_time2
from
    website_url_info;

--  2. LEAD 用于统计窗口内往下第n行值
select
    *,
    -- 获取createtime列向上第2个值, 如果没有就用 夯哥 作为默认值进行填充.
    -- 参1: 要操作的列,   参2: 向下第几个.  参3: 默认值.
    lead(createtime, 2, '夯哥') over(partition by cookieid order by createtime) lead_time
from
    website_url_info;

--  3. FIRST_VALUE 取分组内排序后,截止到当前行,第一个值
select
    *,
    first_value(createtime) over(partition by cookieid order by createtime) first_time
from
    website_url_info;

--  4. LAST_VALUE  取分组内排序后,截止到当前行,最后一个值
select
    *,
    last_value(createtime) over(partition by cookieid order by createtime) last_time
from
    website_url_info;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值