【SQL】SQL-管好你的字符串
- 一、引言
- 二、基础加工类(最高频,必须熟练)
-
- [1. CONCAT / CONCAT_WS:字符串拼接](#1. CONCAT / CONCAT_WS:字符串拼接)
- [2. SUBSTR/SUBSTRING:子串截取](#2. SUBSTR/SUBSTRING:子串截取)
- [3. TRIM / LTRIM / RTRIM:去空格](#3. TRIM / LTRIM / RTRIM:去空格)
- [4. LOWER / UPPER:大小写 这个容易理解](#4. LOWER / UPPER:大小写 这个容易理解)
- 三、填充与替换
-
- [1. LPAD / RPAD:补位对齐](#1. LPAD / RPAD:补位对齐)
- [2. REPLACE / REGEXP_REPLACE:替换](#2. REPLACE / REGEXP_REPLACE:替换)
- 四、正则提取:REGEXP_EXTRACT
- [五、JSON解析:GET_JSON_OBJECT / JSON_TUPLE(爬虫抓取/文本解析贼好用)](#五、JSON解析:GET_JSON_OBJECT / JSON_TUPLE(爬虫抓取/文本解析贼好用))
-
- [1. GET_JSON_OBJECT:单字段提取](#1. GET_JSON_OBJECT:单字段提取)
- [2. JSON_TUPLE:多字段一次提取](#2. JSON_TUPLE:多字段一次提取)
- [六、切分与URL解析(需要理解下LATERAL VIEW EXPLODE)](#六、切分与URL解析(需要理解下LATERAL VIEW EXPLODE))
-
- [1. SPLIT / SPLIT_PART:字符串拆分](#1. SPLIT / SPLIT_PART:字符串拆分)
- [2. PARSE_URL:URL解析 => 网址解析神器](#2. PARSE_URL:URL解析 => 网址解析神器)
- 七、总结对比
一、引言
今天写 Hive 里字符串函数的故事。
数据岗相关的同学,最头疼的一定是数据清洗模块。清洗不到位,计算、分析时会出现各种问题,其中以字符串清洗尤甚:字段涉及拼接、截取、空位处理、JSON解析、URL拆解等......稍不留神,脏数据就悄悄溜进去了。
管好你的字符串,不是说说而已。
- SQL专题往期内容:
二、基础加工类(最高频,必须熟练)
1. CONCAT / CONCAT_WS:字符串拼接
sql
-- CONCAT:简单拼接,无分隔符
SELECT CONCAT('Hello', ' ', '点个关注') AS rt;
-- 输出:Hello 点个关注
-- 拼接多个字段生成业务主键
SELECT CONCAT(region, '_', city, '_', user_id) AS biz_key
FROM user;
-- 输出:region_city_user_id 替换具体值
-- ⚠️ 注意:CONCAT 任一参数为 NULL,结果就是 NULL,不能有脏数据
SELECT CONCAT('Hello', NULL, '点个关注'); -- 返回 NULL
sql
-- CONCAT_WS:带分隔符拼接(WS = With Separator)
SELECT CONCAT_WS('-', '2026', '04', '24') AS date_str;
-- 输出:2026-04-24
-- 多字段用逗号拼接
SELECT CONCAT_WS(',', tag1, tag2, tag3) AS tags
FROM product_tags;
-- ⚠️ CONCAT_WS 会自动跳过 NULL,不会导致整体变 NULL
SELECT CONCAT_WS('-', '2026', NULL, '24'); -- 输出:2026--24
总结:看到差别了么,CONCAT / CONCAT_WS除了null值外,还有重复拼接的差异
2. SUBSTR/SUBSTRING:子串截取
sql
-- SUBSTR(string, start, length)
-- start 从1开始,正数从左、负数从右,长度几位
-- 截取日期中的年月
SELECT SUBSTR('2026-04-24', 1, 7) AS year_month;
-- 输出:2026-04
-- 截取手机号后4位
SELECT SUBSTR('12345678', -4) AS last_4;
-- 输出:5678
3. TRIM / LTRIM / RTRIM:去空格
sql
-- TRIM:去掉两端空格
SELECT TRIM(' Hello 点个关注 ') AS result;
-- 输出:Hello 点个关注
-- LTRIM:只去掉左端空格
SELECT LTRIM(' Hello 点个关注 ') AS result;
-- 输出:Hello World (这里有个空格)
-- RTRIM:只去掉右端空格
SELECT RTRIM(' Hello 点个关注 ') AS result;
-- 输出:(这里有个空格) Hello 点个关注
划重点:
TRIM只去空格,如果要去其他字符可以用TRIM(BOTH 'x' FROM 'xxxHelloxxx'),其他场景可以用REGEXP_REPLACE、REPLACE等其他方式。
4. LOWER / UPPER:大小写 这个容易理解
sql
-- LOWER:转小写
SELECT LOWER('Hello 点个关注') AS rt;
-- 输出:hello 点个关注
-- UPPER:转大写
SELECT UPPER('Hello 点个关注') AS rt;
-- 输出:HELLO 点个关注
三、填充与替换
1. LPAD / RPAD:补位对齐
sql
-- LPAD:左补齐(右侧对齐)
SELECT LPAD('24', 4, '0') AS result;
-- 输出:0024
-- RPAD:右补齐(左侧对齐)
SELECT RPAD('24', 4, 'x') AS result;
-- 输出:24xx
划重点:
LPAD在做报表对齐、编码补零时特别好用。
2. REPLACE / REGEXP_REPLACE:替换
sql
-- REPLACE:简单字符串替换
SELECT REPLACE('Hello 点个关注', '点个关注', '收藏') AS rt;
-- 输出:Hello 收藏
-- 批量替换敏感词
SELECT REPLACE(REPLACE(phone, SUBSTR(phone, 4, 4), '****'), email, '***@***.com') AS info
FROM user;
sql
-- REGEXP_REPLACE:正则替换,灵活度最好
-- 去掉所有非数字字符
SELECT REGEXP_REPLACE('Phone: 123-1234-5678', '[^0-9]', '') AS rt;
-- 输出:12312345678
-- 去掉多余空格(多个连续空格变一个)
SELECT REGEXP_REPLACE('Hello 点个关注 SQL', '\\s+', ' ') AS rt;
-- 输出:Hello 点个关注 SQL
-- 去掉字符串中的 HTML 标签
SELECT REGEXP_REPLACE('<p>Hello <b>点个关注</b></p>', '<[^>]+>', '') AS rt;
-- 输出:Hello 点个关注
sql
-- 实战:手机号脱敏
SELECT phone,
REGEXP_REPLACE(phone, '(\\d{3})\\d{4}(\\d{4})', '$1****$2') AS masked_phone
FROM user;
-- 输出:138****5678
划重点:简单替换用
REPLACE,有规律的替换用REGEXP_REPLACE,正则是最牛的。
四、正则提取:REGEXP_EXTRACT
sql
-- REGEXP_EXTRACT(string, pattern, group)
-- group=0 返回整个匹配,group=1 返回第一个捕获组
-- 提取邮箱域名
SELECT REGEXP_EXTRACT('user@example.com', '@([a-zA-Z0-9.]+)', 1) AS domain;
-- 输出:example.com
-- 提取 URL 中的域名
SELECT REGEXP_EXTRACT('https://www.example.com/path?id=1', 'https?://([^/]+)', 1) AS host;
-- 输出:www.example.com
-- 提取中文字符
SELECT REGEXP_EXTRACT('订单号ORD20260424已发货', '([\\u4e00-\\u9fa5]+)', 1) AS chinese;
-- 输出:订单号
划重点:
REGEXP_EXTRACT配合group参数能精准提取,比SUBSTR+INSTR的组合优雅太多。
五、JSON解析:GET_JSON_OBJECT / JSON_TUPLE(爬虫抓取/文本解析贼好用)
1. GET_JSON_OBJECT:单字段提取
JSON首先得理解Key-Value 的k-v组合形式,键值对。
sql
-- 从 JSON 字符串提取字段
SELECT
GET_JSON_OBJECT('{"name":"四叔","age":100,"city":"杭州"}', '$.name') AS name,
GET_JSON_OBJECT('{"name":"四叔","age":100,"city":"杭州"}', '$.age') AS age,
GET_JSON_OBJECT('{"name":"四叔","age":100,"city":"杭州"}', '$.city') AS city;
-- 输出:四叔 | 100 | 杭州
sql
-- 提取嵌套 JSON
SELECT GET_JSON_OBJECT(
'{"user":{"name":"四叔","address":{"city":"杭州","zip":"100000"}}}',
'$.user.name'
) AS name,
GET_JSON_OBJECT(
'{"user":{"name":"四叔","address":{"city":"杭州","zip":"100000"}}}',
'$.user.address.city'
) AS city;
-- 输出:四叔 | 杭州```
```sql
-- 提取数组元素
SELECT GET_JSON_OBJECT(
'{"items":[{"id":1,"name":"A"},{"id":2,"name":"B"}]}',
'$.items[0].name'
) AS first_item;
-- 输出:A
⚠️ 注意:推荐先执行
SET odps.sql.udf.getjsonobj.new=true;开启新模式,避免转义问题。
2. JSON_TUPLE:多字段一次提取
sql
-- JSON_TUPLE 一次提取多个字段,比多次调用 GET_JSON_OBJECT 会更高效一些
SELECT json_str,
t.name,
t.age,
t.city
FROM user_json
LATERAL VIEW JSON_TUPLE(json_str, 'name', 'age', 'city') t AS name, age, city;
sql
-- 实战:解析埋点日志中的 JSON 字段
SELECT log_id,
t.event_name,
t.page_url,
t.user_id,
t.duration
FROM event_log
LATERAL VIEW JSON_TUPLE(event_data, 'eventName', 'pageUrl', 'userId', 'duration') t AS event_name, page_url, user_id, duration
WHERE dt = '${bizdate}';
划重点:提取1个字段用
GET_JSON_OBJECT,提取多个字段用JSON_TUPLE+LATERAL VIEW,性能更好。
六、切分与URL解析(需要理解下LATERAL VIEW EXPLODE)
1. SPLIT / SPLIT_PART:字符串拆分
sql
-- SPLIT:按分隔符拆分,返回数组
SELECT SPLIT('看,海,的,四,叔', ',') AS result;
-- 输出:["看","海","的","四","叔"]
-- 配合 LATERAL VIEW 展开数组
SELECT user_id,
tag
FROM user_info
LATERAL VIEW EXPLODE(SPLIT(tags, ',')) t AS tag;
sql
-- SPLIT_PART:直接取拆分后的第N部分(从1开始)
SELECT SPLIT_PART('2026-04-24', '-', 1) AS year,
SPLIT_PART('2026-04-24', '-', 2) AS month,
SPLIT_PART('2026-04-24', '-', 3) AS day;
-- 输出:2026 | 04 | 24
划重点:取固定位置用
SPLIT_PART更简洁;需要展开全部元素用SPLIT+EXPLODE。
2. PARSE_URL:URL解析 => 网址解析神器
sql
-- PARSE_URL:提取 URL 的各个部分
SELECT PARSE_URL('https://www.example.com:8080/path/page?id=42&name=test#section', 'HOST') AS host,
PARSE_URL('https://www.example.com:8080/path/page?id=42&name=test#section', 'PATH') AS path,
PARSE_URL('https://www.example.com:8080/path/page?id=42&name=test#section', 'QUERY') AS query,
PARSE_URL('https://www.example.com:8080/path/page?id=42&name=test#section', 'REF') AS ref,
PARSE_URL('https://www.example.com:8080/path/page?id=42&name=test#section', 'PROTOCOL') AS protocol,
PARSE_URL('https://www.example.com:8080/path/page?id=42&name=test#section', 'PORT') AS port;
-- 输出:
-- host: www.example.com
-- path: /path/page
-- query: id=42&name=test
-- ref: section
-- protocol: https
-- port: 8080
sql
-- 提取指定查询参数
SELECT url,
PARSE_URL(url, 'QUERY', 'id') AS param_id,
PARSE_URL(url, 'QUERY', 'name') AS param_name
FROM access_log;
-- 输出:id | name
sql
-- 实战:流量来源分析
SELECT PARSE_URL(referrer_url, 'HOST') AS source_domain,
PARSE_URL(referrer_url, 'QUERY', 'utm_source') AS utm_source,
PARSE_URL(referrer_url, 'QUERY', 'utm_medium') AS utm_medium,
COUNT(*) AS pv
FROM page_view_log
WHERE referrer_url IS NOT NULL
GROUP BY PARSE_URL(referrer_url, 'HOST'),
PARSE_URL(referrer_url, 'QUERY', 'utm_source'),
PARSE_URL(referrer_url, 'QUERY', 'utm_medium')
ORDER BY pv DESC
LIMIT 20;
划重点:
PARSE_URL第三个参数可指定 QUERY 中的具体 key。
七、总结对比
| 模块 | 常用函数 | 典型场景 |
|---|---|---|
| 基础加工 | CONCAT/CONCAT_WS, SUBSTR, TRIM, LOWER/UPPER | 拼接、截取、清洗、统一格式 |
| 填充与替换 | LPAD/RPAD, REPLACE, REGEXP_REPLACE | 补零、脱敏、去特殊字符 |
| 正则提取 | REGEXP_EXTRACT | 日志解析、关键信息提取 |
| JSON解析 | GET_JSON_OBJECT, JSON_TUPLE | 埋点数据、嵌套字段提取 |
| 切分与URL | SPLIT/SPLIT_PART, PARSE_URL | 多值字段展开、流量分析 |
管好你的字符串~ 字符串清洗要在数据入仓的第一层就做好哦,避免脏数据流入。
今天的分享就到这里,觉得有用动动小手点赞+关注+收藏,一键三连~ 有问题留言沟通啦~