第 4 章 广电用户基本信息简单查询
写给新手的开篇
本章是 Hive SQL 的入门核心,承接第 3 章的建表与数据导入,教你从广电用户基本信息表中,精准、高效地拿出想要的数据,是后续所有用户分析、消费统计、行为洞察的基础。
学完本章,你能独立完成 80% 的广电用户基础分析需求:
- 精准查询指定用户的编号、开户时间、状态等核心信息
- 筛选出销户、正常等指定状态的用户清单
- 统计品牌数量、用户等级分布、各状态用户数等核心指标
- 对用户数据进行分组、排序、分页,快速定位 TOP 级数据
- 用正则表达式快速匹配字段,简化复杂查询
前置准备:已完成第 3 章的ZJSM业务库创建、mediamatch_usermsg用户基本表创建、CSV 数据导入,能正常进入 Hive 客户端并切换到ZJSM库。
核心业务表字段回顾:本章所有案例均基于mediamatch_usermsg表,核心字段如下
表格
| 字段名 | 业务含义 |
|---|---|
phone_no | 用户编号(唯一主键,核心关联字段) |
open_time | 用户开户时间 |
run_name | 用户状态(正常、主动销户、被动销户等) |
sm_name | 品牌名称 |
owner_name | 用户等级名称 |
run_time | 用户状态变更时间 |
sm_code | 品牌编号 |
owner_code | 用户等级编号 |
addressoj | 用户完整地址 |
terminal_no | 用户地址编号 |
force | 宽带是否生效 |
前置必执行配置(新手必开,提升查询体验)
-- 切换到广电业务库
USE ZJSM;
-- 开启查询结果列名显示
SET hive.cli.print.header = true;
-- 列名仅显示字段名,不显示表名
SET hive.resultset.use.unique.column.names=false;
-- 开启提示符显示当前数据库,避免建表/查询走错库
SET hive.cli.print.current.db=true;一、模块一:SELECT 基础查询(所有查询的根基)
1. 大白话理解
SELECT...FROM是 Hive 查询最核心的语法,一句话解释:SELECT后面写你想要看的字段,FROM后面写数据来自哪张表。
2. 核心语法
SELECT [ALL | DISTINCT] 字段1, 字段2, 表达式/函数...
FROM 表名 [AS 表别名];3. 分点详解 + 入门案例
(1)基础字段查询
只查询你需要的字段,是生产环境的最佳实践,避免全表扫描浪费性能。
入门案例(任务 4.1):查询广电用户的用户编号及开户时间
-- 代码4-4 精准查询指定字段
SELECT phone_no, open_time
FROM mediamatch_usermsg;
-- 加LIMIT限制返回条数,新手测试必加,避免全量数据刷屏
SELECT phone_no, open_time
FROM mediamatch_usermsg
LIMIT 10;(2)*通配符的使用与禁忌
*代表表中所有字段,新手测试时可以用,但生产环境严禁滥用。
-- 代码4-2 查询表中所有字段
SELECT *
FROM mediamatch_usermsg
LIMIT 5;✅ 新手避坑:
- 生产环境用
*会扫描全表所有字段,大数据量下会造成严重的性能浪费,甚至任务卡死 - 仅在测试表结构、验证数据是否导入成功时使用,业务查询必须精准指定字段
(3)表别名的使用
给表起一个简短的别名,能简化 SQL 书写,尤其后续多表关联查询时必备。
-- 代码4-3 表别名使用示例
SELECT t.phone_no, t.open_time, t.run_name
FROM mediamatch_usermsg AS t -- AS关键字可省略,直接写mediamatch_usermsg t
LIMIT 10;✅ 新手避坑:
- 表别名不能用 Hive 关键字,否则会语法报错
- 中文别名必须用反单引号
包裹,比如FROM mediamatch_usermsg AS用户表 - 表别名不会影响查询性能,仅为了提升 SQL 可读性
二、模块二:WHERE 条件过滤(精准筛选数据)
1. 大白话理解
SELECT...FROM会返回全表数据,而WHERE关键字就是给查询加一道「过滤网」,只留下符合你要求的数据,是提升查询效率、精准定位数据的核心。
2. 核心语法
SELECT 字段1,字段2...
FROM 表名
WHERE 过滤条件;- 过滤条件可以是一个或多个,多个条件用
AND/OR连接 - 只有满足条件、返回结果为
TRUE的数据,才会被保留到最终结果中
3. 6 种常用过滤条件 + 广电业务案例
(1)关系运算符过滤
最基础的等值、大小比较,适用于数值、字符串的精准匹配。
表格
| 运算符 | 含义 |
|---|---|
= | 相等匹配 |
!=/<> | 不相等匹配 |
>/< | 大于 / 小于 |
>=/<= | 大于等于 / 小于等于 |
入门案例:查询指定用户编号的用户信息
-- 代码4-5 等值匹配查询
SELECT phone_no, open_time, run_name
FROM mediamatch_usermsg
WHERE phone_no = '2032485';(2)IN 关键字:集合匹配
判断字段值是否在指定的集合中,替代多个OR等值条件,简化 SQL。
-- 代码4-6 IN关键字查询多个指定用户
SELECT phone_no, open_time, run_name
FROM mediamatch_usermsg
WHERE phone_no IN ('2116612','2017186','2017187','2082337');
-- 反向匹配:NOT IN,查询不在集合中的用户
SELECT phone_no, open_time, run_name
FROM mediamatch_usermsg
WHERE phone_no NOT IN ('2116612','2017186');(3)BETWEEN AND:区间匹配
判断字段值是否在指定闭区间内,适用于数值、日期的范围筛选。
-- 代码4-7 查询用户编号在指定区间内的用户
SELECT phone_no, open_time
FROM mediamatch_usermsg
WHERE phone_no BETWEEN '2032485' AND '2032500';✅ 新手避坑:
BETWEEN AND是闭区间,包含起始值和结束值- 必须保证
值1 < 值2,否则区间无效,查询不到任何数据
(4)NULL 关键字:空值判断
专门用来判断字段是否为空值,注意空值和 0、空字符串有本质区别。
-- 代码4-8 查询用户编号不为空的有效用户
SELECT phone_no
FROM mediamatch_usermsg
WHERE phone_no IS NOT NULL;
-- 查询地址为空的用户
SELECT phone_no, addressoj
FROM mediamatch_usermsg
WHERE addressoj IS NULL;✅ 新手避坑:
- 绝对不能用
字段 = NULL判断空值,Hive 中任何值和 NULL 做=比较,结果都是 NULL,会查不到数据 - 正确写法只有
IS NULL/IS NOT NULL
(5)LIKE 关键字:模糊匹配
用来做文本的模糊查询,配合通配符实现灵活匹配,是业务中最常用的过滤方式。
核心通配符:
%:匹配任意长度的字符串,包括空字符串_:匹配单个字符
核心业务案例(任务 4.2):查询所有销户状态的用户
用户销户分为「主动销户」和「被动销户」,都以「销户」结尾,用%通配符完美匹配。
-- 代码4-12 查询销户状态的用户基本信息
SELECT phone_no, run_name, open_time
FROM mediamatch_usermsg
WHERE run_name LIKE '%销户';
-- 拓展:查询品牌名称以「宽频」结尾的用户
SELECT phone_no, sm_name
FROM mediamatch_usermsg
WHERE sm_name LIKE '%宽频';
-- 拓展:_匹配单个字符,查询用户编号第2位是2,结尾是296756的用户
SELECT phone_no, sm_name
FROM mediamatch_usermsg
WHERE phone_no LIKE '_296756';(6)RLIKE 关键字:正则匹配
比 LIKE 更强大的模糊匹配,支持正则表达式,适合复杂的文本匹配场景。
-- 代码4-11 正则匹配品牌名称包含「珠江」或「甜果」的用户
SELECT phone_no, sm_name
FROM mediamatch_usermsg
WHERE sm_name RLIKE '.*(珠江|甜果).*';三、模块三:DISTINCT 去重 + 聚合函数(数据统计核心)
1. DISTINCT 关键字:去除重复数据
大白话理解
DISTINCT会过滤掉查询结果中的重复数据,只保留唯一值,适合统计种类、去重清单等场景。
核心语法 + 案例
-- 单字段去重:查询所有不重复的用户状态
SELECT DISTINCT run_name
FROM mediamatch_usermsg;
-- 代码4-13 多字段去重:只有用户状态+用户等级都相同时,才会被去重
SELECT DISTINCT run_name, owner_name
FROM mediamatch_usermsg;✅ 新手避坑:
DISTINCT必须放在所有字段的最前面,不能写在字段中间- 多字段去重时,只有所有字段的值都完全相同,才会被判定为重复数据
2. 聚合函数:数据统计计算
聚合函数是对一组数据进行统计计算,最终返回一个统计结果,是业务指标统计的核心工具。
常用聚合函数 + 广电业务案例
表格
| 函数 | 核心作用 | 广电业务场景 |
|---|---|---|
COUNT(*)/COUNT(1) | 统计总行数 | 统计用户总数、各状态用户数 |
COUNT(DISTINCT 字段) | 统计去重后的行数 | 统计品牌种类个数、不重复的用户等级数 |
MIN(字段) | 取字段最小值 | 最早开户时间、最小用户编号 |
MAX(字段) | 取字段最大值 | 最晚状态变更时间、最大用户编号 |
AVG(字段) | 取字段平均值 | 平均开户时长(需配合日期函数) |
SUM(字段) | 取字段值总和 | 消费总额统计(后续账单表用) |
核心业务案例(任务 4.3):统计用户基本信息表中品牌名称的种类个数
-- 代码4-15 统计品牌名称种类(去重后计数)
SELECT COUNT(DISTINCT sm_name) AS brand_count
FROM mediamatch_usermsg;
-- 拓展:查询最小的用户编号
SELECT MIN(phone_no)
FROM mediamatch_usermsg;
-- 拓展:统计所有有效用户总数
SELECT COUNT(*) AS user_total
FROM mediamatch_usermsg
WHERE phone_no IS NOT NULL;四、模块四:列别名(让查询结果更易读)
1. 大白话理解
给查询的字段、计算结果、聚合函数结果起一个新名字,尤其是把英文字段转成中文,让查询结果一目了然,不用再猜英文字段的含义。
2. 核心语法
SELECT 字段名 [AS] 列别名
FROM 表名;AS关键字可省略,直接写字段名 别名即可- 中文别名必须用反单引号 `` 包裹
3. 入门配置 + 业务案例
(1)前置配置:开启列名显示
Hive CLI 默认不显示查询结果的列名,必须先执行以下配置开启:
-- 代码4-16 开启查询结果列名显示
SET hive.cli.print.header = true;
-- 代码4-17 列名仅显示字段名,不显示表名前缀
SET hive.resultset.use.unique.column.names=false;✅ 新手提示:以上配置仅当前会话有效,退出 Hive 客户端后重新登录需要重新执行;想永久生效,需要修改hive-site.xml配置文件。
(2)核心业务案例(任务 4.4):统计不同客户等级名称的数据记录数
-- 代码4-20 统计不同客户等级的用户数,设置中文列别名
SELECT
owner_name AS `客户等级名称`,
COUNT(*) AS `记录数`
FROM mediamatch_usermsg
GROUP BY owner_name;
-- 代码4-19 简单中文别名示例
SELECT sm_name AS `品牌名称`
FROM mediamatch_usermsg
LIMIT 5;4. 新手必守规则
WHERE、GROUP BY、HAVING关键字中不能使用列别名,因为 Hive 执行顺序中,这些关键字先于SELECT执行,识别不到别名ORDER BY排序时,必须使用列别名,不能使用原始表达式- 别名不能使用 Hive 关键字,中文别名必须用反单引号包裹
五、模块五:GROUP BY 分组查询(分类统计核心)
1. 大白话理解
GROUP BY会把数据按指定的字段分成不同的组,比如按「用户状态」分组、按「用户等级」分组,然后对每个分组单独做聚合统计,实现「分类统计」的需求。
2. 核心语法
SELECT 分组字段, 聚合函数
FROM 表名
[WHERE 分组前过滤条件]
GROUP BY 分组字段1, 分组字段2...;3. 核心铁则(新手必背,否则必报错)
SELECT关键字后面的字段,要么出现在 GROUP BY 的分组字段中,要么被聚合函数包裹,两者必须满足其一,否则会直接语法报错。
错误案例 vs 正确案例
-- 代码4-21 错误示例:SELECT里的phone_no既不在GROUP BY里,也没被聚合函数包裹,必报错
SELECT phone_no, run_name, sm_name
FROM mediamatch_usermsg
GROUP BY run_name, sm_name;
-- 代码4-22 正确示例:SELECT里的字段和GROUP BY里的完全一致
SELECT run_name, sm_name
FROM mediamatch_usermsg
GROUP BY run_name, sm_name;
-- 代码4-23 正确示例:非分组字段用聚合函数包裹
SELECT COUNT(phone_no) AS user_count, run_name, sm_name
FROM mediamatch_usermsg
GROUP BY run_name, sm_name;4. 核心业务案例(任务 4.5):统计不同用户状态的数据记录数
-- 代码4-24 按用户状态分组,统计每个状态的用户总数
SELECT
run_name AS `用户状态`,
COUNT(*) AS `用户数`
FROM mediamatch_usermsg
GROUP BY run_name;5. 新手避坑
GROUP BY可以按多个字段分组,只有所有分组字段的值都相同时,才会被分到同一组WHERE是分组前过滤,先过滤掉不符合条件的数据,再对剩下的数据分组,WHERE里不能用聚合函数
六、模块六:HAVING 关键字(分组结果过滤)
1. 大白话理解
HAVING是对GROUP BY分组后的聚合结果进行过滤,专门解决「WHERE 里不能用聚合函数」的问题。
2. WHERE vs HAVING 核心区别(新手必懂)
表格
| 关键字 | 过滤时机 | 能否使用聚合函数 | 适用场景 |
|---|---|---|---|
| WHERE | 分组前过滤,先过滤再分组 | 不能 | 原始数据的行级过滤 |
| HAVING | 分组后过滤,先分组再过滤 | 必须配合聚合函数使用 | 分组统计结果的过滤 |
3. 核心业务案例(任务 4.6):统计用户数量大于 200 的客户等级
-- 代码4-26 分组后筛选用户数大于200的客户等级
SELECT
owner_name AS `客户等级名称`,
COUNT(*) AS `用户数`
FROM mediamatch_usermsg
GROUP BY owner_name
-- 对分组后的聚合结果过滤,只保留用户数>200的等级
HAVING COUNT(*) > 200;
-- 拓展:筛选用户数大于10000的用户状态
SELECT COUNT(*) AS nums, run_name
FROM mediamatch_usermsg
GROUP BY run_name
HAVING COUNT(*) > 10000;七、模块七:排序 + LIMIT 分页(结果排序与条数限制)
1. 4 种排序关键字大白话区别
Hive 有 4 种排序关键字,新手先重点掌握最常用的ORDER BY,其他了解特性即可。
表格
| 关键字 | 核心特性 | 适用场景 |
|---|---|---|
ORDER BY | 全局排序,对所有数据统一排序,结果完全有序 | 小数据集精准排序、TOP N 统计,生产环境必须配合 LIMIT 使用 |
SORT BY | Reducer 内局部排序,仅保证单个 Reducer 内数据有序,整体结果无序 | 大数据集局部排序,不要求全局有序 |
DISTRIBUTE BY | 控制数据按哈希值分发到不同 Reducer,不负责排序 | 配合 SORT BY 实现分区内排序,解决数据倾斜 |
CLUSTER BY | 等价于DISTRIBUTE BY + SORT BY(同字段升序) | 同字段的分发与排序简化写法 |
2. ORDER BY 全局排序(最常用)
核心语法
SELECT 字段1, 聚合函数...
FROM 表名
[WHERE/GROUP BY/HAVING]
ORDER BY 排序字段1 [ASC|DESC], 排序字段2 [ASC|DESC]...;ASC:升序排列(默认值,不写就是升序)DESC:降序排列- 多字段排序:先按第一个字段排序,第一个字段值相同时,再按第二个字段排序
3. LIMIT 关键字:限制结果条数 + 分页
核心语法
SELECT 字段...
FROM 表名
LIMIT [偏移量,] 返回条数;- 偏移量:可选,代表从第几条数据开始返回,默认从 0 开始
- 返回条数:必填,限制最终返回的数据行数
入门案例
-- 代码4-27 只返回前2条数据
SELECT *
FROM mediamatch_usermsg
LIMIT 2;
-- 代码4-28 分页查询:从第3条数据开始,返回2条(偏移量2,返回2条)
SELECT *
FROM mediamatch_usermsg
LIMIT 2,2;4. 核心业务案例(任务 4.7):统计用户人数最多的 3 种用户状态
-- 代码4-31 统计用户数TOP3的用户状态
SELECT
COUNT(*) as nums,
run_name AS `用户状态`
FROM mediamatch_usermsg
GROUP BY run_name
-- 按用户数降序排列
ORDER BY nums DESC
-- 只取前3条
LIMIT 3;5. 新手避坑
- 大数据量下用
ORDER BY不加 LIMIT,会导致所有数据进入一个 Reducer 处理,任务长时间卡死,生产环境严禁这么做 - Hive 严格模式下(
hive.mapred.mode=strict),ORDER BY必须配合 LIMIT 使用 DISTRIBUTE BY必须写在SORT BY前面,语法顺序不能乱
八、模块八:正则表达式模糊匹配字段名(进阶技巧)
1. 大白话理解
当表的字段很多,我们想批量匹配符合规则的字段时(比如所有以_time结尾的时间字段),可以用正则表达式模糊匹配字段名,不用一个个手动写。
2. 前置配置 + 核心语法
(1)必须先开的配置
-- 代码4-33 开启正则表达式匹配字段名支持
set hive.support.quoted.identifiers = none;不开这个配置,正则表达式会被识别为普通字符串,无法匹配字段。
(2)核心语法
正则表达式必须用 ** 反单引号 ``** 包裹,不能用单引号 / 双引号。
3. 核心业务案例(任务 4.8):查询客户发生状态变更的时间及开户时间
两个时间字段都以_time结尾,用正则.*_time直接匹配所有符合规则的字段。
-- 代码4-35 查询所有以_time结尾的时间字段
SELECT `.*_time`
FROM mediamatch_usermsg
LIMIT 10;
-- 代码4-34 正则匹配所有以phone开头的字段
SELECT `phone.*`
FROM mediamatch_usermsg
LIMIT 10;九、新手高频踩坑避坑指南
字段别名使用错误
- 坑:在 WHERE/GROUP BY 里用列别名,导致报错
- 解决:严格遵守执行顺序,WHERE/GROUP BY 用原始字段名,只有 ORDER BY 能用别名
空值判断错误
- 坑:用
字段 = NULL判断空值,查不到任何数据 - 解决:空值判断必须用
IS NULL/IS NOT NULL
- 坑:用
GROUP BY 语法报错
- 坑:SELECT 里的字段既不在 GROUP BY 里,也没被聚合函数包裹
- 解决:严格遵守铁则,SELECT 后的字段要么在 GROUP BY 里,要么用聚合函数包裹
LIKE 通配符用错
- 坑:把通配符
%写成*,导致模糊匹配失效 - 解决:LIKE 语法用
%匹配任意字符,*仅在正则里生效
- 坑:把通配符
大数据量排序卡死
- 坑:千万级数据用 ORDER BY 不加 LIMIT,导致任务长时间运行
- 解决:ORDER BY 必须配合 LIMIT 使用,大数据量用 DISTRIBUTE BY + SORT BY 做局部排序
正则匹配失效
- 坑:没开配置、用单引号包裹正则,导致匹配不到字段
- 解决:先开启
hive.support.quoted.identifiers = none,正则用反单引号 `` 包裹
十、入门自测题(学完检验成果)
题目 1
请写出 SQL,查询所有正常使用状态(run_name 包含「正常」)的用户编号、用户状态、开户时间、用户地址,只返回前 20 条数据。
参考答案
SELECT
phone_no AS `用户编号`,
run_name AS `用户状态`,
open_time AS `开户时间`,
addressoj AS `用户地址`
FROM mediamatch_usermsg
WHERE run_name LIKE '%正常%'
LIMIT 20;题目 2
请写出 SQL,统计每个品牌的用户数量,按用户数量降序排列,只返回用户数大于 500 的品牌。
参考答案
SELECT
sm_name AS `品牌名称`,
COUNT(*) AS `用户数`
FROM mediamatch_usermsg
GROUP BY sm_name
HAVING COUNT(*) > 500
ORDER BY `用户数` DESC;题目 3
请写出 SQL,查询用户表中所有不重复的用户等级,按等级名称升序排列。
参考答案
SELECT DISTINCT owner_name AS `用户等级`
FROM mediamatch_usermsg
ORDER BY `用户等级` ASC;题目 4
请写出 SQL,用正则表达式匹配用户表中所有以name结尾的字段,返回前 10 条数据。
参考答案
-- 先开启正则支持
SET hive.support.quoted.identifiers = none;
-- 匹配所有以name结尾的字段
SELECT `.*name`
FROM mediamatch_usermsg
LIMIT 10;