Add one-skill

This commit is contained in:
Xin Wang
2026-05-13 11:03:00 +08:00
parent a4c8b29176
commit f9e36ef92d
34 changed files with 7656 additions and 0 deletions

View File

@@ -0,0 +1,459 @@
# 聚合模式速查
## 基本聚合
### 聚合函数列表
| 函数 | 说明 | 示例 |
|------|------|------|
| COUNT(*) | 计数含NULL行 | 总行数 |
| COUNT(col) | 计数不含NULL | 有效数据数 |
| COUNT(DISTINCT col) | 去重计数 | 用户数 |
| SUM(col) | 求和 | 总销售额 |
| AVG(col) | 平均值 | 平均薪资 |
| MIN(col) | 最小值 | 最小年龄 |
| MAX(col) | 最大值 | 最高分 |
### 基本用法
```sql
-- 单列聚合
SELECT
COUNT(*) AS total_rows,
COUNT(DISTINCT user_id) AS unique_users,
SUM(amount) AS total_amount,
AVG(amount) AS avg_amount,
MIN(amount) AS min_amount,
MAX(amount) AS max_amount
FROM orders
-- 分组聚合
SELECT
department,
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY department
```
---
## GROUP BY
```sql
-- 单字段分组
SELECT
category,
COUNT(*) AS count
FROM products
GROUP BY category
-- 多字段分组
SELECT
category,
brand,
COUNT(*) AS count,
SUM(price) AS total_price
FROM products
GROUP BY category, brand
-- 分组 + 排序
SELECT
department,
COUNT(*) AS count
FROM employees
GROUP BY department
ORDER BY count DESC
-- 分组 + LIMIT取Top N组
SELECT
category,
COUNT(*) AS count
FROM products
GROUP BY category
ORDER BY count DESC
LIMIT 10
```
---
## HAVING分组过滤
```sql
-- HAVING vs WHERE
-- WHERE过滤原始行GROUP BY 前)
-- HAVING过滤分组结果GROUP BY 后)
-- 示例筛选订单数大于10的用户
SELECT
user_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 10
-- 多条件 HAVING
SELECT
department,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 5000
AND COUNT(*) >= 5
-- HAVING + ORDER BY
SELECT
user_id,
COUNT(*) AS order_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 5
ORDER BY order_count DESC
```
---
## 去重计数
```sql
-- COUNT(DISTINCT)
SELECT
COUNT(DISTINCT user_id) AS unique_users
FROM orders
-- 分组去重计数
SELECT
date,
COUNT(DISTINCT user_id) AS unique_users,
COUNT(*) AS total_orders
FROM orders
GROUP BY date
-- 多字段去重计数
SELECT
COUNT(DISTINCT user_id, product_id) AS unique_user_product_pairs
FROM order_items
-- 大数据量近似去重(性能优化)
SELECT
approx_count_distinct(user_id) AS approx_unique_users
FROM orders
```
---
## 集合聚合(数组结果)
```sql
-- collect_list收集为数组不去重
SELECT
user_id,
collect_list(product_id) AS products
FROM orders
GROUP BY user_id
-- collect_set收集为数组去重
SELECT
user_id,
collect_set(product_id) AS unique_products
FROM orders
GROUP BY user_id
-- 取数组大小
SELECT
user_id,
size(collect_list(product_id)) AS product_count,
size(collect_set(product_id)) AS unique_product_count
FROM orders
GROUP BY user_id
```
---
## 多级聚合ROLLUP / CUBE / GROUPING SETS
### ROLLUP层级汇总
```sql
-- 从右到左递减分组级别
SELECT
COALESCE(year, '总计') AS year,
COALESCE(month, '全年') AS month,
COALESCE(region, '全国') AS region,
SUM(sales) AS total_sales
FROM sales_data
GROUP BY ROLLUP (year, month, region)
-- 结果包含:
-- 1. year + month + region 分组
-- 2. year + month 汇总region为NULL
-- 3. year 汇总month,region为NULL
-- 4. 全表汇总year,month,region为NULL
```
### CUBE全维度组合
```sql
-- 所有分组组合
SELECT
COALESCE(year, '总计') AS year,
COALESCE(month, '全月') AS month,
COALESCE(region, '全国') AS region,
SUM(sales) AS total_sales
FROM sales_data
GROUP BY CUBE (year, month, region)
-- 结果包含所有组合:
-- year+month+region, year+month, year+region, month+region
-- year, month, region, 全表汇总
```
### GROUPING SETS自定义组合
```sql
-- 指定分组组合
SELECT
year,
month,
region,
SUM(sales) AS total_sales
FROM sales_data
GROUP BY GROUPING SETS (
(year, month),
(year, region),
(region),
()
)
-- 等价于多个 GROUP BY 合并
```
---
## GROUPING 函数(判断汇总级别)
```sql
-- GROUPING(col)判断该列是否为汇总产生的NULL
-- 0 = 真实值, 1 = 汇总NULL
SELECT
year,
month,
SUM(sales) AS total_sales,
GROUPING(year) AS is_year_total,
GROUPING(month) AS is_month_total
FROM sales_data
GROUP BY ROLLUP (year, month)
-- 用 GROUPING 区分真实NULL和汇总NULL
SELECT
CASE WHEN GROUPING(region) = 1 THEN '全国汇总' ELSE region END AS region,
SUM(sales) AS total_sales
FROM sales_data
GROUP BY ROLLUP (region)
```
---
## 条件聚合CASE WHEN + 聚合)
```sql
-- 分条件统计
SELECT
date,
COUNT(*) AS total_orders,
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS completed_count,
SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled_count,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_count
FROM orders
GROUP BY date
-- 分条件求和
SELECT
department,
SUM(salary) AS total_salary,
SUM(CASE WHEN gender = 'M' THEN salary ELSE 0 END) AS male_salary,
SUM(CASE WHEN gender = 'F' THEN salary ELSE 0 END) AS female_salary
FROM employees
GROUP BY department
-- 条件平均值
SELECT
category,
AVG(CASE WHEN price > 100 THEN price ELSE NULL END) AS high_price_avg
FROM products
GROUP BY category
```
---
## 聚合 + 窗口函数
```sql
-- 分组内占比
SELECT
department,
salary,
ROUND(salary / SUM(salary) OVER (PARTITION BY department) * 100, 2) AS salary_pct
FROM employees
-- 分组累计
SELECT
date,
department,
amount,
SUM(amount) OVER (PARTITION BY department ORDER BY date) AS cumulative
FROM sales
-- 分组排名
SELECT
*,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank
FROM employees
```
---
## 多表聚合
```sql
-- JOIN 后聚合
SELECT
u.department,
COUNT(o.id) AS order_count,
SUM(o.amount) AS total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.department
-- 子查询聚合
SELECT
dept_stats.department,
dept_stats.avg_salary,
emp_count.employee_count
FROM (
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
) dept_stats
JOIN (
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department
) emp_count ON dept_stats.department = emp_count.department
```
---
## 常见问题
### 问题1GROUP BY 字段遗漏
```sql
-- 错误SELECT 字段不在 GROUP BY 中
SELECT
department,
name, -- 错误name 未分组
AVG(salary)
FROM employees
GROUP BY department
-- 解决1添加到 GROUP BY
SELECT
department,
name,
AVG(salary)
FROM employees
GROUP BY department, name
-- 解决2使用聚合函数处理
SELECT
department,
collect_list(name) AS names, -- 收集所有name
AVG(salary)
FROM employees
GROUP BY department
```
### 问题2NULL 影响聚合
```sql
-- COUNT(*) 包含 NULL 行
-- COUNT(col) 不包含 NULL
SELECT
COUNT(*) AS total, -- 包含 NULL 行
COUNT(amount) AS valid, -- 不包含 amount 为 NULL 的行
COUNT(DISTINCT amount) AS unique_values
FROM orders
-- SUM/AVG/MIN/MAX 自动忽略 NULL
SELECT AVG(price) FROM products -- NULL 自动排除
```
### 问题3聚合结果精度
```sql
-- AVG 可能精度丢失
SELECT
AVG(amount) AS avg_amount -- 可能精度不够
AVG(CAST(amount AS DECIMAL(18,6))) AS precise_avg -- 高精度
FROM orders
-- ROUND 控制精度
SELECT
ROUND(AVG(amount), 2) AS avg_amount
FROM orders
```
---
## 聚合性能优化
```sql
-- 1. 先过滤再聚合
SELECT
department,
COUNT(*) AS count
FROM employees
WHERE hire_date >= '2024-01-01' -- 先过滤
GROUP BY department
-- 2. 大数据量用近似聚合
SELECT
approx_count_distinct(user_id) AS users -- 比 COUNT(DISTINCT) 快
FROM orders
-- 3. 减少分组字段数量
SELECT
category, -- 减少分组字段
COUNT(*) AS count
FROM products
GROUP BY category -- 比 GROUP BY category, brand 快
-- 4. 避免复杂计算在 GROUP BY 前
SELECT
department,
AVG(salary * 1.1) AS adjusted_avg -- 先计算再聚合
FROM employees
GROUP BY department
```
---
## 聚合模式选择指南
| 需求 | 推荐方式 |
|------|----------|
| 简单统计 | GROUP BY + 聚合函数 |
| 分条件统计 | CASE WHEN + SUM/COUNT |
| 去重计数 | COUNT(DISTINCT) |
| 大数据去重 | approx_count_distinct |
| 收集数组 | collect_list / collect_set |
| 层级汇总 | ROLLUP |
| 全维度汇总 | CUBE |
| 自定义组合 | GROUPING SETS |
| 分组内计算 | 窗口函数 |
| 多条件过滤 | HAVING |

View File

@@ -0,0 +1,320 @@
# Apache Doris SQL 语法参考
## 数据类型
| 类型 | 说明 | 示例 |
|------|------|------|
| BOOLEAN | 布尔 | active BOOLEAN |
| TINYINT | 1字节整数 | level TINYINT |
| SMALLINT | 2字节整数 | age SMALLINT |
| INT | 4字节整数 | count INT |
| BIGINT | 8字节整数 | id BIGINT |
| LARGEINT | 16字节整数 | hash_key LARGEINT |
| FLOAT | 4字节浮点 | score FLOAT |
| DOUBLE | 8字节浮点 | price DOUBLE |
| DECIMAL(p,s) | 定点数 | amount DECIMAL(18,2) |
| DATE | 日期 | birth_date DATE |
| DATETIME | 日期时间(精确到秒) | created_at DATETIME |
| CHAR(n) | 定长字符串 | code CHAR(10) |
| VARCHAR(n) | 变长字符串 | name VARCHAR(100) |
| STRING | 变长字符串(无长度限制) | description STRING |
| BITMAP | 位图(精确去重) | user_bitmap BITMAP |
| HLL | HyperLogLog近似去重 | user_hll HLL |
| JSON | JSON 数据 | props JSON |
| ARRAY\<type\> | 数组 | tags ARRAY\<STRING\> |
| MAP\<k,v\> | 映射 | props MAP\<STRING,STRING\> |
| STRUCT\<field:type,...\> | 结构体 | info STRUCT\<id:INT,name:STRING\> |
---
## 时间函数
```sql
-- 当前时间
NOW() -- 当前日期时间
CURDATE() -- 当前日期
CURRENT_TIMESTAMP() -- 当前时间戳
-- 格式转换
DATE_FORMAT(date_col, '%Y-%m-%d') -- 日期格式化
DATE_FORMAT(datetime_col, '%Y-%m-%d %H:%i:%s') -- 时间格式化
STR_TO_DATE(str, '%Y-%m-%d') -- 字符串转日期
-- 日期计算
DATE_ADD(date_col, INTERVAL 7 DAY) -- 加7天
DATE_SUB(date_col, INTERVAL 7 DAY) -- 减7天
DATEDIFF(end_date, start_date) -- 日期差(天数)
TIMESTAMPDIFF(unit, start, end) -- 时间差(指定单位)
TIMESTAMPADD(unit, interval, datetime) -- 时间加
-- 日期提取
YEAR(date_col) -- 年
MONTH(date_col) -- 月
DAY(date_col) -- 日
HOUR(datetime_col) -- 时
MINUTE(datetime_col) -- 分
SECOND(datetime_col) -- 秒
QUARTER(date_col) -- 季度 (1-4)
WEEK(date_col) -- 年中第几周
DAYOFWEEK(date_col) -- 周几 (1=周日)
DAYOFYEAR(date_col) -- 年中第几天
-- Unix 时间戳
UNIX_TIMESTAMP() -- 当前 Unix 时间戳
UNIX_TIMESTAMP(datetime_col) -- 转换为 Unix 时间戳
FROM_UNIXTIME(timestamp) -- Unix 时间戳转时间字符串
FROM_UNIXTIME(timestamp, fmt) -- 带格式的转换
-- Doris 日期格式符(不同于 Spark
-- %Y: 4位年, %m: 2位月, %d: 2位日
-- %H: 24小时制, %i: 分钟, %s: 秒
-- %j: 年中天数, %W: 周名
```
---
## 字符串函数
```sql
-- 常用函数
CONCAT(str1, str2, ...) -- 字符串拼接
CONCAT_WS('-', str1, str2, ...) -- 用分隔符拼接
LOWER(str) -- 转小写
UPPER(str) -- 转大写
TRIM(str) -- 去两端空格
LTRIM(str) -- 去左空格
RTRIM(str) -- 去右空格
LENGTH(str) -- 字符串长度
CHAR_LENGTH(str) -- 字符数(中文友好)
SUBSTRING(str, pos, len) -- 截取字符串pos从1开始
LEFT(str, len) -- 取左边len个字符
RIGHT(str, len) -- 取右边len个字符
REVERSE(str) -- 反转字符串
REPEAT(str, n) -- 重复n次
SPACE(n) -- 生成n个空格
-- 查找与替换
INSTR(str, substr) -- 查找子串位置
LOCATE(substr, str, pos) -- 从pos位置查找
REPLACE(str, old, new) -- 替换
REGEXP_EXTRACT(str, pattern, idx) -- 正则提取(注意和 Spark 参数不同)
REGEXP_REPLACE(str, pattern, replacement) -- 正则替换
-- 分割
SPLIT_BY_STRING(str, delimiter) -- 分割Doris 特有)
SPLIT_PART(str, delimiter, idx) -- 取分割后的第idx部分
-- 其他
INITCAP(str) -- 首字母大写
LPAD(str, len, pad) -- 左填充
RPAD(str, len, pad) -- 右填充
HEX(str) -- 转16进制
UNHEX(str) -- 16进制转字符串
```
---
## 聚合函数
```sql
-- 基础聚合
COUNT(*) -- 计数含NULL行
COUNT(col) -- 计数不含NULL
COUNT(DISTINCT col) -- 去重计数
SUM(col) -- 求和
AVG(col) -- 平均值
MIN(col) -- 最小值
MAX(col) -- 最大值
-- 集合聚合
GROUP_CONCAT(col SEPARATOR ',') -- 字符串聚合(类似 collect_list
APPROX_COUNT_DISTINCT(col) -- 近似去重计数
-- 统计函数
VARIANCE(col) -- 方差
VAR_POP(col) -- 总体方差
VAR_SAMP(col) -- 样本方差
STDDEV(col) -- 标准差
STDDEV_POP(col) -- 总体标准差
STDDEV_SAMP(col) -- 样本标准差
-- Bitmap 精确去重Doris 特有)
-- 用于 Aggregate Key 模型中定义为 BITMAP 的列
-- bitmap_union_count(bitmap_col) -- 精确去重计数
-- bitmap_union(bitmap_col) -- 合并 bitmap
-- bitmap_hash(col) -- 将值转为 bitmap用于写入
-- HLL 近似去重Doris 特有)
-- hll_union_agg(hll_col) -- 近似去重计数
-- hll_cardinality(hll_col) -- 返回 HLL 基数
-- hll_hash(col) -- 将值转为 HLL用于写入
```
---
## 条件表达式
```sql
-- CASE WHEN
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE default_result
END
-- CASE 字段匹配
CASE field
WHEN value1 THEN result1
WHEN value2 THEN result2
ELSE default_result
END
-- COALESCE取第一个非空值
COALESCE(col1, col2, default_value)
-- NULLIF相等返回NULL
NULLIF(col1, col2)
-- IF简单条件
IF(condition, true_value, false_value)
-- IFNULL空值替换等同 NVL
IFNULL(col, default_value)
```
---
## JSON 函数
```sql
-- 解析与提取
JSON_PARSE(json_str) -- 解析 JSON 字符串
JSON_EXTRACT(json_str, '$.field') -- 提取 JSON 字段(返回 JSON 类型)
JSON_EXTRACT_STRING(json_str, '$.field') -- 提取 JSON 字段(返回 STRING
JSON_EXTRACT_INT(json_str, '$.field') -- 提取 JSON 字段(返回 INT
JSON_EXTRACT_DOUBLE(json_str, '$.field') -- 提取 JSON 字段(返回 DOUBLE
JSON_EXTRACT_BOOL(json_str, '$.field') -- 提取 JSON 字段(返回 BOOLEAN
-- 路径语法
-- $ : 根节点
-- $.field : 对象字段
-- $[0] : 数组索引
-- $.a.b : 嵌套字段
-- 构造
JSON_OBJECT('key1', val1, 'key2', val2) -- 构造 JSON 对象
JSON_ARRAY(val1, val2, ...) -- 构造 JSON 数组
-- 查询
JSON_LENGTH(json_str) -- JSON 长度
JSON_KEYS(json_str) -- JSON 所有 key
JSON_VALID(json_str) -- 是否合法 JSON
```
---
## ARRAY 函数
```sql
-- 创建
ARRAY(val1, val2, ...) -- 创建数组
-- 访问
array_contains(arr, val) -- 判断是否包含
element_at(arr, idx) -- 取元素idx从1开始
arr[idx] -- 取元素idx从0开始
-- 操作
SIZE(arr) -- 数组长度
ARRAY_JOIN(arr, delimiter) -- 数组转字符串
CONCAT(arr1, arr2) -- 数组拼接
-- 展开与排序
EXPLODE(arr) -- 展开数组为多行LATERAL VIEW 中使用)
ARRAY_SORT(arr) -- 排序
ARRAY_DISTINCT(arr) -- 去重
```
---
## 与 Spark SQL 的主要差异
| 特性 | Spark SQL | Apache Doris | 说明 |
|------|-----------|-------------|------|
| **日期格式符** | `yyyy-MM-dd` | `%Y-%m-%d` | Doris 用 MySQL 风格格式符 |
| **当前时间** | `current_timestamp()` | `NOW()``CURRENT_TIMESTAMP()` | 都支持Doris 偏好 NOW() |
| **日期加减** | `date_add(col, 7)` | `DATE_ADD(col, INTERVAL 7 DAY)` | Doris 需要 INTERVAL 语法 |
| **正则提取** | `regexp_extract(str, pattern, idx)` | `REGEXP_EXTRACT(str, pattern, idx)` | 参数名可能不同 |
| **LEFT SEMI JOIN** | ✅ 支持 | ❌ 不支持 | 用 IN 子查询替代 |
| **LEFT ANTI JOIN** | ✅ 支持 | ❌ 不支持 | 用 NOT IN / NOT EXISTS 替代 |
| **INSERT OVERWRITE** | ✅ 支持 | ✅ 2.0+ 支持 | 早期版本不支持 |
| **CTE (WITH)** | ✅ 支持 | ✅ 支持 | 都支持 |
| **MERGE INTO** | ✅ 支持 | ❌ 不支持 | Doris 用 UPSERT 替代 |
| **临时表链式处理** | ✅ 推荐 | ✅ 统一规范 | 编码规范层面统一禁止 CTE |
| **精确去重** | COUNT(DISTINCT) | COUNT(DISTINCT) 或 BITMAP | Doris BITMAP 性能更好 |
| **近似去重** | `approx_count_distinct` | HLL 类型 | Doris 内置 HLL 类型 |
| **UPDATE/DELETE** | ❌ 不支持 | ✅ 支持 | Doris 支持 DML |
| **UPSERT** | ❌ 不支持 | ❌ 不直接支持 | 用 Aggregate/Unique Key 模型替代 |
| **分页** | LIMIT无OFFSET | LIMIT + OFFSET | Doris 完整支持 |
| **JSON** | `get_json_object` | `JSON_EXTRACT_STRING` 等系列函数 | 函数名完全不同 |
| **collect_list/set** | ✅ 支持 | ❌ 不直接支持 | 用 GROUP_CONCAT 替代 |
| **LATERAL VIEW explode** | ✅ 支持 | ✅ 支持 | 语法兼容 |
---
## SQL 生成规则
### 通用规则(所有引擎统一)
1. **禁止使用 CTE (WITH 子句)**,每个主要逻辑步骤必须物化为临时表
2. **先 DROP 再 CREATE**`DROP TABLE IF EXISTS ...; CREATE TABLE ... AS SELECT ...;`
3. **禁止 `SELECT *`**,必须明确列出所有字段
4. 多表查询时所有表必须使用简短别名
5. 每个步骤前添加注释说明
6. **谓词下推**过滤条件前置JOIN 时在 WHERE 中一并添加过滤
7. 临时表命名:`${db_tmp_env}.tmp_{业务简称}_{步骤序号}`
### Doris 特有规则
1. 使用 `INSERT INTO` 写入目标表Doris 2.0+ 也支持 `INSERT OVERWRITE`
2. Aggregate Key 表自动合并相同 Key 的数据
3. Unique Key 表自动按主键去重,保留最新数据
4. 不支持 `LEFT SEMI JOIN` / `LEFT ANTI JOIN`,用 `IN` / `NOT IN` 替代
5. 精确去重推荐 `BITMAP`,近似去重推荐 `HLL`
6. 日期函数用 MySQL 风格:
- `DATE_FORMAT(col, '%Y-%m-%d')`(不是 `yyyy-MM-dd`
- `DATE_ADD(col, INTERVAL 7 DAY)`(不是 `date_add(col, 7)`
- `CURDATE()` / `NOW()`
7. 不支持 `collect_list` / `collect_set`,用 `GROUP_CONCAT` 替代
8. 时间范围筛选:
```sql
-- 日账期过滤
WHERE stat_date = '${day_id}'
-- 最近N个月
WHERE stat_date >= DATE_FORMAT(DATE_SUB(STR_TO_DATE('${month_id}', '%Y%m'), INTERVAL N MONTH), '%Y%m')
AND stat_date < '${month_id}'
```
### SQL 脚本结构
```sql
-- =====================================================================
-- @SqlName: doris-D-SQL-{表名}
-- @Engine: doris
-- ...(头注释)
-- =====================================================================
-- Step01: {步骤描述}
DROP TABLE IF EXISTS ${db_tmp_env}.tmp_xxx_01;
CREATE TABLE ${db_tmp_env}.tmp_xxx_01 AS
SELECT ...;
-- Step02: {步骤描述}
DROP TABLE IF EXISTS ${db_tmp_env}.tmp_xxx_02;
CREATE TABLE ${db_tmp_env}.tmp_xxx_02 AS
SELECT ...;
-- 最后一步:写入目标表
INSERT INTO ${db_eda_env}.target_table
SELECT ...;
```

View File

@@ -0,0 +1,334 @@
# Hive SQL 语法参考
## 数据类型
| 类型 | 说明 | 示例 |
|------|------|------|
| TINYINT | 1字节整数 | level TINYINT |
| SMALLINT | 2字节整数 | age SMALLINT |
| INT | 4字节整数 | count INT |
| BIGINT | 8字节整数 | id BIGINT |
| FLOAT | 4字节浮点 | score FLOAT |
| DOUBLE | 8字节浮点 | price DOUBLE |
| DECIMAL(p,s) | 定点数 | amount DECIMAL(18,2) |
| BOOLEAN | 布尔 | active BOOLEAN |
| STRING | 变长字符串 | name STRING |
| VARCHAR(n) | 变长字符串(限长) | code VARCHAR(50) |
| CHAR(n) | 定长字符串 | flag CHAR(1) |
| DATE | 日期 | birth_date DATE |
| TIMESTAMP | 时间戳(纳秒精度) | created_at TIMESTAMP |
| BINARY | 二进制 | data BINARY |
| ARRAY\<type\> | 数组 | tags ARRAY\<STRING\> |
| MAP\<k,v\> | 映射 | props MAP\<STRING,STRING\> |
| STRUCT\<f1:t1,...\> | 结构体 | user STRUCT\<id:INT,name:STRING\> |
| UNIONTYPE\<t1,t2,...\> | 联合类型 | value UNIONTYPE\<INT,STRING\> |
---
## 时间函数
```sql
-- 当前时间
current_date() -- 当前日期
current_timestamp() -- 当前时间戳
unix_timestamp() -- 当前 Unix 时间戳(秒)
-- 格式转换
date_format(date_col, 'yyyy-MM-dd') -- 日期格式化
date_format(timestamp_col, 'yyyy-MM-dd HH:mm:ss') -- 时间格式化
to_date(string_col) -- 字符串转日期
to_date(string_col, 'yyyy-MM-dd') -- 字符串转日期(带格式)
from_unixtime(timestamp) -- Unix 时间戳转字符串
from_unixtime(timestamp, 'yyyy-MM-dd') -- 带格式转换
-- 日期计算
date_add(date_col, 7) -- 加7天
date_sub(date_col, 7) -- 减7天
add_months(date_col, 3) -- 加3个月
datediff(end_date, start_date) -- 日期差(天数)
months_between(date1, date2) -- 月份差
-- 日期提取
year(date_col) -- 年
month(date_col) -- 月
day(date_col) -- 日
dayofmonth(date_col) -- 月中第几天
dayofweek(date_col) -- 周几 (1=周日, 7=周六)
hour(timestamp_col) -- 时
minute(timestamp_col) -- 分
second(timestamp_col) -- 秒
quarter(date_col) -- 季度 (1-4)
weekofyear(date_col) -- 年中第几周
last_day(date_col) -- 月末日期
trunc(date_col, 'MM') -- 月初日期
trunc(date_col, 'YY') -- 年初日期
-- Unix 时间戳
unix_timestamp(date_col) -- 转 Unix 时间戳
unix_timestamp(string_col, 'yyyy-MM-dd') -- 指定格式转换
from_unixtime(timestamp) -- Unix 时间戳转字符串
```
---
## 字符串函数
```sql
-- 常用函数
concat(str1, str2, ...) -- 字符串拼接
concat_ws('-', str1, str2, ...) -- 用分隔符拼接
lower(str) -- 转小写
upper(str) -- 转大写
trim(str) -- 去两端空格
ltrim(str) -- 去左空格
rtrim(str) -- 去右空格
length(str) -- 字符串长度
substring(str, pos, len) -- 截取字符串pos从1开始
substr(str, pos, len) -- 同 substring
left(str, len) -- 取左边len个字符
right(str, len) -- 取右边len个字符
reverse(str) -- 反转字符串
repeat(str, n) -- 重复n次
space(n) -- 生成n个空格
-- 查找与替换
instr(str, substr) -- 查找子串位置
locate(substr, str, pos) -- 从pos位置查找
replace(str, old, new) -- 替换
regexp_extract(str, pattern, idx) -- 正则提取
regexp_replace(str, pattern, replacement) -- 正则替换
-- 分割
split(str, delimiter) -- 分割成数组
-- 其他
initcap(str) -- 首字母大写
lpad(str, len, pad) -- 左填充
rpad(str, len, pad) -- 右填充
hex(col) -- 转16进制
unhex(str) -- 16进制转字符串
```
---
## 聚合函数
```sql
-- 基础聚合
COUNT(*) -- 计数含NULL行
COUNT(col) -- 计数不含NULL
COUNT(DISTINCT col) -- 去重计数
SUM(col) -- 求和
AVG(col) -- 平均值
MIN(col) -- 最小值
MAX(col) -- 最大值
-- 集合聚合
collect_list(col) -- 返回数组(不去重)
collect_set(col) -- 返回数组(去重)
-- 统计函数
variance(col) -- 方差
var_pop(col) -- 总体方差
var_samp(col) -- 样本方差
stddev(col) -- 标准差
stddev_pop(col) -- 总体标准差
stddev_samp(col) -- 样本标准差
-- 近似函数
approx_count_distinct(col) -- 近似去重计数(大数据量优化)
-- 其他
first(col) -- 第一个值
last(col) -- 最后一个值
```
---
## 条件表达式
```sql
-- CASE WHEN
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE default_result
END
-- CASE 字段匹配
CASE field
WHEN value1 THEN result1
WHEN value2 THEN result2
ELSE default_result
END
-- COALESCE取第一个非空值
COALESCE(col1, col2, default_value)
-- NULLIF相等返回NULL
NULLIF(col1, col2)
-- IF简单条件
IF(condition, true_value, false_value)
-- NVL空值替换
NVL(col, default_value)
```
---
## 复杂类型操作
```sql
-- ARRAY 操作
array(val1, val2, ...) -- 创建数组
array_contains(arr, val) -- 判断是否包含
element_at(arr, idx) -- 取元素idx从1开始
arr[idx] -- 取元素idx从0开始
size(arr) -- 数组长度
array_join(arr, delimiter) -- 数组转字符串
sort_array(arr) -- 排序
array_distinct(arr) -- 去重
-- 展开LATERAL VIEW + explode
-- 展开数组
SELECT id, tag
FROM table
LATERAL VIEW explode(tags) t AS tag;
-- 展开数组带索引
SELECT id, pos, tag
FROM table
LATERAL VIEW posexplode(tags) t AS pos, tag;
-- 展开 Map
SELECT id, map_key, map_value
FROM table
LATERAL VIEW explode(props) m AS map_key, map_value;
-- MAP 操作
map(key1, val1, key2, val2) -- 创建 Map
str_to_map(str, delim1, delim2) -- 字符串转 Map
map_contains(map, key) -- 判断是否包含key
map_keys(map) -- 所有 key返回数组
map_values(map) -- 所有 value返回数组
size(map) -- Map大小
-- STRUCT 操作
named_struct('name1', val1, 'name2', val2) -- 创建结构体
struct_col.field_name -- 访问结构体字段
```
---
## 分区表操作
```sql
-- 创建分区表
CREATE TABLE target_table (
id BIGINT,
name STRING,
amount DECIMAL(18,2)
)
PARTITIONED BY (day_id STRING)
STORED AS ORC;
-- 静态分区写入
INSERT OVERWRITE TABLE target_table
PARTITION (day_id = '${day_id}')
SELECT id, name, amount FROM source_table;
-- 动态分区写入
SET hive.exec.dynamic.partition = true;
SET hive.exec.dynamic.partition.mode = nonstrict;
INSERT OVERWRITE TABLE target_table
PARTITION (day_id)
SELECT id, name, amount, day_id FROM source_table;
-- 分区管理
SHOW PARTITIONS target_table;
ALTER TABLE target_table ADD IF NOT EXISTS PARTITION (day_id = '2026-05-10');
ALTER TABLE target_table DROP IF EXISTS PARTITION (day_id = '2026-01-01');
-- MSCK REPAIR恢复分区元数据
MSCK REPAIR TABLE target_table;
```
---
## 与 Spark SQL 的主要差异
| 特性 | Spark SQL | Hive | 说明 |
|------|-----------|------|------|
| **LEFT SEMI JOIN** | ✅ 独立语法 | ✅ 支持(语义相同) | Hive 也可用 IN 子查询替代 |
| **LEFT ANTI JOIN** | ✅ 独立语法 | ✅ 支持(语义相同) | Hive 也可用 NOT IN 替代 |
| **CTE (WITH)** | ✅ 支持 | ✅ Hive 0.13+ 支持 | 都支持但 Hive 中推荐物化临时表 |
| **INSERT OVERWRITE** | ✅ 支持 | ✅ 支持 | 写法一致 |
| **MERGE INTO** | ✅ 支持 | ❌ 不支持 | Hive 不支持 |
| **UPDATE/DELETE** | ❌ 不支持 | 仅 ACID 表支持 | 普通 Hive 表不支持 |
| **collect_list/set** | ✅ 支持 | ✅ 支持 | 完全一致 |
| **LATERAL VIEW** | ✅ 支持 | ✅ 支持Hive 原生) | Hive 首创的语法 |
| **分桶 JOIN** | 可优化 | 可优化SMB JOIN | Hive 分桶优化更成熟 |
| **日期格式** | `yyyy-MM-dd` | `yyyy-MM-dd` | 格式一致 |
| **临时表** | CREATE TEMP TABLE | CREATE TEMPORARY TABLE | 关键字略有不同 |
| **存储格式** | PARQUET/ORC | ORC/PARQUET/TEXTFILE | Hive 支持 TEXTFILE |
| **分区发现** | 自动 | 需 MSCK REPAIR 或 ALTER | Hive 需手动恢复 |
| **复杂类型** | 完整支持 | 完整支持 | 基本一致 |
| **窗口函数** | ✅ 完整支持 | ✅ 完整支持 | 语法一致 |
---
## SQL 生成规则
### 通用规则(所有引擎统一)
1. **禁止使用 CTE (WITH 子句)**,每个主要逻辑步骤必须物化为临时表
2. **先 DROP 再 CREATE**`DROP TABLE IF EXISTS ...; CREATE TABLE ... AS SELECT ...;`
3. **禁止 `SELECT *`**,必须明确列出所有字段
4. 多表查询时所有表必须使用简短别名
5. 每个步骤前添加注释说明
6. **谓词下推**过滤条件前置JOIN 时在 WHERE 中一并添加过滤
7. 临时表命名:`${db_tmp_env}.tmp_{业务简称}_{步骤序号}`
### Hive 特有规则
1. 使用 `INSERT OVERWRITE TABLE ... PARTITION (...)` 写入目标表
2. 动态分区需先 `SET hive.exec.dynamic.partition = true;`
3. 分区列不能出现在表定义的列中Hive 特有约束)
4. 支持 `collect_list` / `collect_set` 聚合
5. 支持 `LATERAL VIEW explode()` 展开数组
6. 日期函数:`date_format()`, `to_date()`, `date_add()`, `add_months()`(和 Spark 一致)
7. 时间范围筛选:
```sql
-- 日账期过滤
WHERE day_id = '${day_id}'
-- 最近N个月月份格式 yyyyMM
WHERE month_id >= date_format(add_months(to_date('${month_id}', 'yyyyMM'), -N), 'yyyyMM')
AND month_id < '${month_id}'
```
### SQL 脚本结构
```sql
-- =====================================================================
-- @SqlName: hive-D-SQL-{表名}
-- @Engine: hive
-- ...(头注释)
-- =====================================================================
-- Step01: {步骤描述}
DROP TABLE IF EXISTS ${db_tmp_env}.tmp_xxx_01;
CREATE TABLE ${db_tmp_env}.tmp_xxx_01 AS
SELECT ...;
-- Step02: {步骤描述}
DROP TABLE IF EXISTS ${db_tmp_env}.tmp_xxx_02;
CREATE TABLE ${db_tmp_env}.tmp_xxx_02 AS
SELECT ...;
-- 最后一步:写入目标表
INSERT OVERWRITE TABLE ${db_eda_env}.target_table
PARTITION (day_id = '${day_id}')
SELECT ...;
```

View File

@@ -0,0 +1,369 @@
# JOIN 模式速查
## JOIN 类型
| 类型 | 说明 | 结果特点 |
|------|------|----------|
| INNER JOIN | 内连接 | 只返回匹配的行 |
| LEFT JOIN | 左外连接 | 左表全部右表匹配无匹配为NULL |
| RIGHT JOIN | 右外连接 | 右表全部左表匹配无匹配为NULL |
| FULL OUTER JOIN | 全外连接 | 两表全部无匹配处为NULL |
| CROSS JOIN | 交叉连接 | 笛卡尔积(每行与每行组合) |
| LEFT SEMI JOIN | 左半连接 | 左表中在右表有匹配的行(不返回右表列) |
| LEFT ANTI JOIN | 左反连接 | 左表中在右表无匹配的行 |
---
## INNER JOIN
```sql
-- 基本语法
SELECT a.*, b.*
FROM table_a a
INNER JOIN table_b b ON a.id = b.id
-- 等价写法(逗号连接)
SELECT a.*, b.*
FROM table_a a, table_b b
WHERE a.id = b.id
-- 多字段关联
SELECT a.*, b.*
FROM table_a a
INNER JOIN table_b b
ON a.user_id = b.user_id
AND a.order_date = b.order_date
```
**使用场景**:只需要两表都有匹配的数据时使用。
---
## LEFT JOIN
```sql
-- 基本语法
SELECT a.*, b.*
FROM table_a a
LEFT JOIN table_b b ON a.id = b.id
-- 处理右表NULL值
SELECT
a.id,
a.name,
COALESCE(b.amount, 0) AS amount, -- NULL转0
IF(b.id IS NULL, '无匹配', '有匹配') AS match_status
FROM table_a a
LEFT JOIN table_b b ON a.id = b.id
-- 找出左表中无匹配的行(差集)
SELECT a.*
FROM table_a a
LEFT JOIN table_b b ON a.id = b.id
WHERE b.id IS NULL
```
**使用场景**:保留左表所有数据,右表补充信息时使用。
---
## RIGHT JOIN
```sql
-- 基本语法
SELECT a.*, b.*
FROM table_a a
RIGHT JOIN table_b b ON a.id = b.id
-- 等价于 LEFT JOIN 反过来
SELECT a.*, b.*
FROM table_b b
LEFT JOIN table_a a ON b.id = a.id
```
**使用场景**:保留右表所有数据时使用(可改用 LEFT JOIN 反转)。
---
## FULL OUTER JOIN
```sql
-- 基本语法
SELECT
COALESCE(a.id, b.id) AS id, -- 统一ID
a.name,
b.amount
FROM table_a a
FULL OUTER JOIN table_b b ON a.id = b.id
-- 找出两表差异
SELECT
a.id AS a_id,
b.id AS b_id,
CASE
WHEN a.id IS NULL THEN '仅在B表'
WHEN b.id IS NULL THEN '仅在A表'
ELSE '两表都有'
END AS status
FROM table_a a
FULL OUTER JOIN table_b b ON a.id = b.id
WHERE a.id IS NULL OR b.id IS NULL
```
**使用场景**:需要两表完整数据,分析差异时使用。
---
## CROSS JOIN
```sql
-- 基本语法(笛卡尔积)
SELECT a.name, b.color
FROM products a
CROSS JOIN colors b
-- 结果products每行与colors每行组合
-- products有10行colors有5行 → 结果50行
-- 等价写法
SELECT a.name, b.color
FROM products a, colors b -- 无WHERE条件
```
**使用场景**:生成所有组合、测试数据生成时使用。
**注意**:数据量大时慎用,可能产生巨量结果。
---
## LEFT SEMI JOINSpark SQL 特有)
```sql
-- 基本语法
SELECT a.*
FROM table_a a
LEFT SEMI JOIN table_b b ON a.id = b.id
-- 效果返回A表中在B表有匹配的行不返回B表的列
-- 等价于 IN 子查询
SELECT a.*
FROM table_a a
WHERE a.id IN (SELECT id FROM table_b b)
```
**使用场景**:只需要判断左表是否在右表存在,不需要右表数据。
---
## LEFT ANTI JOINSpark SQL 特有)
```sql
-- 基本语法
SELECT a.*
FROM table_a a
LEFT ANTI JOIN table_b b ON a.id = b.id
-- 效果返回A表中在B表无匹配的行
-- 等价于 NOT IN 子查询
SELECT a.*
FROM table_a a
WHERE a.id NOT IN (SELECT id FROM table_b b)
-- 或 NOT EXISTS
SELECT a.*
FROM table_a a
WHERE NOT EXISTS (SELECT 1 FROM table_b b WHERE b.id = a.id)
```
**使用场景**:找出差集(左表中不存在于右表的数据)。
---
## 多表 JOIN
```sql
-- 三表关联
SELECT
o.order_id,
u.user_name,
p.product_name,
oi.quantity
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.id
-- 四表及以上
SELECT
a.col1,
b.col2,
c.col3,
d.col4
FROM table_a a
JOIN table_b b ON a.id = b.a_id
JOIN table_c c ON b.id = c.b_id
JOIN table_d d ON c.id = d.c_id
```
**建议**:多表 JOIN 时,从最小表开始,逐步关联。
---
## 自连接Self Join
```sql
-- 员工与经理关联
SELECT
e.name AS employee,
m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id
-- 查找重复数据
SELECT
a.id,
a.name,
b.id AS duplicate_id
FROM table_a a
JOIN table_a b ON a.name = b.name AND a.id < b.id
-- 紧邻数据比较(前后行)
SELECT
a.date,
a.amount,
b.amount AS prev_amount
FROM sales a
LEFT JOIN sales b ON a.date = b.date + 1
```
---
## JOIN 条件下推优化
**原则**:过滤条件前置,减少 JOIN 数据量。
```sql
-- 推荐:过滤前置
SELECT a.*, b.*
FROM table_a a
JOIN table_b b ON a.id = b.id
WHERE a.date = '${day_id}' -- 先过滤A表
AND b.status = 'active' -- 先过滤B表
-- 不推荐JOIN后再过滤
SELECT a.*, b.*
FROM (
SELECT * FROM table_a -- 未过滤
) a
JOIN (
SELECT * FROM table_b -- 未过滤
) b ON a.id = b.id
WHERE a.date = '${day_id}'
AND b.status = 'active'
```
**性能差异**
- 推荐JOIN 前各表已过滤数据量小JOIN 快
- 不推荐:全表 JOIN 后过滤,数据量大,性能差
---
## JOIN 常见问题
### 问题1关联字段类型不一致
```sql
-- 错误STRING 与 BIGINT 关联
SELECT a.*, b.*
FROM table_a a
JOIN table_b b ON a.id = b.id -- a.id是STRINGb.id是BIGINT
-- 解决:类型转换
SELECT a.*, b.*
FROM table_a a
JOIN table_b b ON CAST(a.id AS BIGINT) = b.id
```
### 问题2关联字段含NULL
```sql
-- 问题NULL关联不上
SELECT a.*, b.*
FROM table_a a
LEFT JOIN table_b b ON a.ref_id = b.id -- a.ref_id有NULL
-- 解决先过滤NULL或用COALESCE
SELECT a.*, b.*
FROM table_a a
LEFT JOIN table_b b ON COALESCE(a.ref_id, 'N/A') = b.id
```
### 问题3多字段关联效率低
```sql
-- 问题:多字段关联
SELECT a.*, b.*
FROM table_a a
JOIN table_b b
ON a.user_id = b.user_id
AND a.order_date = b.order_date
AND a.product_id = b.product_id
-- 解决:合并关联字段
SELECT a.*, b.*
FROM table_a a
JOIN table_b b
ON CONCAT(a.user_id, '_', a.order_date, '_', a.product_id)
= CONCAT(b.user_id, '_', b.order_date, '_', b.product_id)
```
### 问题4大表 JOIN 大表
```sql
-- 问题两表都很大JOIN 慢
SELECT a.*, b.*
FROM large_table_a a
JOIN large_table_b b ON a.id = b.id
-- 解决方案:
-- 1. 尽量前置过滤
-- 2. 使用分区表,按分区关联
-- 3. 调整 Spark 并行度
-- 4. 使用 BROADCAST JOIN其中一表较小
```
---
## BROADCAST JOIN小表广播
```sql
-- Spark SQL 自动判断(需配置阈值)
-- 小表自动广播到所有节点,避免 Shuffle
-- 手动指定广播
SELECT /*+ BROADCAST(b) */ a.*, b.*
FROM large_table_a a
JOIN small_table_b b ON a.id = b.id
-- 多表广播
SELECT /*+ BROADCAST(b), BROADCAST(c) */ a.*, b.*, c.*
FROM large_table_a a
JOIN small_table_b b ON a.id = b.id
JOIN small_table_c c ON a.category = c.category
```
**适用条件**:其中一表数据量较小(通常 < 10MB
---
## JOIN 类型选择指南
| 需求 | 推荐 JOIN | 说明 |
|------|-----------|------|
| 两表都有才保留 | INNER JOIN | 最常用 |
| 左表全部保留 | LEFT JOIN | 补充右表信息 |
| 右表全部保留 | RIGHT JOIN | 或反转用 LEFT JOIN |
| 两表全部保留 | FULL OUTER JOIN | 分析差异 |
| 判断左表是否在右表存在 | LEFT SEMI JOIN | 不需要右表列 |
| 左表不在右表的行 | LEFT ANTI JOIN | 差集查询 |
| 生成所有组合 | CROSS JOIN | 慎用 |

View File

@@ -0,0 +1,336 @@
# Kudu (via Impala) SQL 语法参考
> **重要**Kudu 本身没有 SQL 引擎,通过 Impala 访问。本文档是 Impala SQL 操作 Kudu 表的语法参考。
## 数据类型
| 类型 | 说明 | 示例 |
|------|------|------|
| BOOLEAN | 布尔 | active BOOLEAN |
| TINYINT | 1字节整数 | level TINYINT |
| SMALLINT | 2字节整数 | age SMALLINT |
| INT | 4字节整数 | count INT |
| BIGINT | 8字节整数 | id BIGINT |
| FLOAT | 4字节浮点 | score FLOAT |
| DOUBLE | 8字节浮点 | price DOUBLE |
| DECIMAL(p,s) | 定点数 | amount DECIMAL(18,2) |
| STRING | 变长字符串 | name STRING |
| VARCHAR(n) | 变长字符串(限长) | code VARCHAR(50) |
| CHAR(n) | 定长字符串 | flag CHAR(1) |
| TIMESTAMP | 时间戳(微秒精度) | created_at TIMESTAMP |
| DATE | 日期 | birth_date DATE |
| BINARY | 二进制 | data BINARY |
**注意**Kudu 不支持 ARRAY、MAP、STRUCT 等复杂类型。
---
## 时间函数
```sql
-- 当前时间
NOW() -- 当前日期时间
CURRENT_TIMESTAMP() -- 当前时间戳
UNIX_TIMESTAMP() -- 当前 Unix 时间戳(秒)
TO_DATE(NOW()) -- 当前日期
-- 格式转换
FROM_UNIXTIME(timestamp, 'yyyy-MM-dd') -- Unix 时间戳转格式化字符串
FROM_UNIXTIME(timestamp, 'yyyy-MM-dd HH:mm:ss')
CAST(string_col AS TIMESTAMP) -- 字符串转时间戳
CAST(timestamp_col AS STRING) -- 时间戳转字符串
-- 日期计算
DAYS_ADD(date_col, 7) -- 加7天
DAYS_SUB(date_col, 7) -- 减7天
ADD_MONTHS(date_col, 3) -- 加3个月Impala 6.0+
DATE_ADD(date_col, INTERVAL 7 DAY) -- 加7天标准语法
DATEDIFF(end_date, start_date) -- 日期差(天数)
MONTHS_BETWEEN(date1, date2) -- 月份差
-- 日期提取
YEAR(date_col) -- 年
MONTH(date_col) -- 月
DAY(date_col) -- 日
DAYOFWEEK(date_col) -- 周几 (1=周日)
DAYOFYEAR(date_col) -- 年中第几天
HOUR(timestamp_col) -- 时
MINUTE(timestamp_col) -- 分
SECOND(timestamp_col) -- 秒
QUARTER(date_col) -- 季度 (1-4)
WEEKOFYEAR(date_col) -- 年中第几周
-- Impala 日期格式符
-- yyyy: 4位年, MM: 2位月, dd: 2位日
-- HH: 24小时制, mm: 分钟, ss: 秒
```
---
## 字符串函数
```sql
-- 常用函数
CONCAT(str1, str2) -- 字符串拼接仅2个参数
CONCAT_WS('-', str1, str2, ...) -- 用分隔符拼接
LOWER(str) -- 转小写
UPPER(str) -- 转大写
TRIM(str) -- 去两端空格
LTRIM(str) -- 去左空格
RTRIM(str) -- 去右空格
LENGTH(str) -- 字符串长度
SUBSTR(str, pos, len) -- 截取字符串pos从1开始
SUBSTRING(str, pos, len) -- 同上
LEFT(str, len) -- 取左边len个字符
RIGHT(str, len) -- 取右边len个字符
REVERSE(str) -- 反转字符串
REPEAT(str, n) -- 重复n次
SPACE(n) -- 生成n个空格
-- 查找与替换
INSTR(str, substr) -- 查找子串位置
LOCATE(substr, str, pos) -- 从pos位置查找
REPLACE(str, old, new) -- 替换
REGEXP_EXTRACT(str, pattern, idx) -- 正则提取
REGEXP_REPLACE(str, pattern, replacement) -- 正则替换
-- 分割
SPLIT_PART(str, delimiter, idx) -- 取分割后第idx部分
-- 其他
INITCAP(str) -- 首字母大写
LPAD(str, len, pad) -- 左填充
RPAD(str, len, pad) -- 右填充
HEX(col) -- 转16进制
UNHEX(str) -- 16进制转字符串
```
**注意**Impala 的 `CONCAT` 只接受 2 个参数,多参数拼接用 `CONCAT_WS`
---
## 聚合函数
```sql
-- 基础聚合
COUNT(*) -- 计数含NULL行
COUNT(col) -- 计数不含NULL
COUNT(DISTINCT col) -- 去重计数
SUM(col) -- 求和
AVG(col) -- 平均值
MIN(col) -- 最小值
MAX(col) -- 最大值
-- 集合聚合
GROUP_CONCAT(col SEPARATOR ',') -- 字符串聚合
-- 统计函数
VARIANCE(col) -- 方差
VAR_POP(col) -- 总体方差
VAR_SAMP(col) -- 样本方差
STDDEV(col) -- 标准差
STDDEV_POP(col) -- 总体标准差
STDDEV_SAMP(col) -- 样本标准差
-- 近似函数
NDV(col) -- 近似去重计数Impala 特有,比 COUNT(DISTINCT) 快)
APPROX_COUNT_DISTINCT(col) -- 近似去重计数
-- 其他
FIRST_VALUE(col) -- 窗口内第一个值
LAST_VALUE(col) -- 窗口内最后一个值
```
---
## 条件表达式
```sql
-- CASE WHEN
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE default_result
END
-- CASE 字段匹配
CASE field
WHEN value1 THEN result1
WHEN value2 THEN result2
ELSE default_result
END
-- COALESCE取第一个非空值
COALESCE(col1, col2, default_value)
-- NULLIF相等返回NULL
NULLIF(col1, col2)
-- IF简单条件
IF(condition, true_value, false_value)
-- ISNULL / ISNOTNULL
ISNULL(col) -- 判断是否为NULL
ISNOTNULL(col) -- 判断是否不为NULL
-- NVL空值替换
NVL(col, default_value)
```
---
## Kudu 特有操作
### INSERT INTO
```sql
-- 追加写入
INSERT INTO kudu_table VALUES (1, 'test', 100.00);
INSERT INTO kudu_table SELECT * FROM other_table WHERE ...;
```
### UPSERT INTOKudu 核心能力)
```sql
-- 主键存在则更新,不存在则插入
UPSERT INTO kudu_table VALUES (1, 'test', 100.00);
UPSERT INTO kudu_table SELECT * FROM staging_table WHERE ...;
```
### UPDATE
```sql
-- 更新数据(主键列不可更新)
UPDATE kudu_table SET status = 'active' WHERE id = 1;
UPDATE kudu_table SET amount = amount * 1.1 WHERE date < '2026-01-01';
```
### DELETE
```sql
-- 删除数据
DELETE FROM kudu_table WHERE id = 1;
DELETE FROM kudu_table WHERE date < '2026-01-01';
```
### ALTER TABLEKudu 特有)
```sql
-- 添加列
ALTER TABLE kudu_table ADD COLUMNS (new_col STRING COMMENT '新列');
-- 删除列
ALTER TABLE kudu_table DROP COLUMN old_col;
-- 修改列类型
ALTER TABLE kudu_table CHANGE old_name new_name STRING;
-- 添加范围分区
ALTER TABLE kudu_table ADD RANGE PARTITION
'2026-06-01' <= VALUES < '2026-07-01';
-- 删除范围分区
ALTER TABLE kudu_table DROP RANGE PARTITION
'2026-01-01' <= VALUES < '2026-02-01';
```
---
## 分区操作
```sql
-- Hash 分区(建表时指定)
PARTITION BY HASH(id) PARTITIONS 8
-- Range 分区(建表时指定)
PARTITION BY RANGE(stat_date) (
PARTITION '2026-01-01' <= VALUES < '2026-02-01',
PARTITION '2026-02-01' <= VALUES < '2026-03-01'
)
-- Hash + Range 组合
PARTITION BY
HASH(id) PARTITIONS 4,
RANGE(stat_date) (...)
```
---
## 与 Spark SQL / Hive 的主要差异
| 特性 | Spark SQL | Hive | Kudu (Impala) | 说明 |
|------|-----------|------|--------------|------|
| **INSERT OVERWRITE** | ✅ | ✅ | ❌ | Kudu 不支持,用 DELETE + INSERT 替代 |
| **UPSERT** | ❌ | ❌ | ✅ | Kudu 独有核心能力 |
| **UPDATE** | ❌ | 仅ACID表 | ✅ | Kudu 原生支持 |
| **DELETE** | ❌ | 仅ACID表 | ✅ | Kudu 原生支持 |
| **主键约束** | ❌ 无约束 | ❌ 无约束 | ✅ 强制主键 | Kudu 表必须有主键 |
| **复杂类型** | ✅ ARRAY/MAP/STRUCT | ✅ 完整支持 | ❌ 不支持 | Kudu 限制 |
| **CONCAT 多参数** | ✅ 任意个数 | ✅ 任意个数 | ❌ 仅2个参数 | Impala 用 CONCAT_WS |
| **近似去重** | `approx_count_distinct` | `approx_count_distinct` | `NDV` | Impala 特有函数名 |
| **临时表链式处理** | ✅ 推荐 | ✅ 推荐 | ❌ 不需要 | Kudu 用 UPSERT 单步 |
| **CTE (WITH)** | ✅ 支持 | ✅ 支持 | ✅ 支持 | 都支持 |
| **MERGE INTO** | ✅ 支持 | ❌ | ❌ | 用 UPSERT 替代 |
| **分区类型** | 目录分区 | 目录分区 | Hash/Range 内置 | Kudu 分区机制不同 |
| **分桶** | 可选 | 可选 | Hash 分区替代 | 概念类似但实现不同 |
| **日期格式** | `yyyy-MM-dd` | `yyyy-MM-dd` | `yyyy-MM-dd` | 格式一致 |
| **collect_list/set** | ✅ | ✅ | ❌ | Impala 用 GROUP_CONCAT |
| **ALTER ADD COLUMN** | ✅ | ✅ | ✅ | Kudu 支持在线加列 |
| **ALTER DROP COLUMN** | 部分支持 | ✅ | ✅ | Kudu 支持在线删列 |
---
## SQL 生成规则
### 通用规则(所有引擎统一)
1. **禁止使用 CTE (WITH 子句)**,每个主要逻辑步骤必须物化为临时表
2. **先 DROP 再 CREATE**`DROP TABLE IF EXISTS ...; CREATE TABLE ... AS SELECT ...;`
3. **禁止 `SELECT *`**,必须明确列出所有字段
4. 多表查询时所有表必须使用简短别名
5. 每个步骤前添加注释说明
6. **谓词下推**过滤条件前置JOIN 时在 WHERE 中一并添加过滤
7. 临时表命名:`${db_tmp_env}.tmp_{业务简称}_{步骤序号}`
### Kudu 特有规则
1. **Kudu 表不支持 `INSERT OVERWRITE`**,用 `DELETE + INSERT``UPSERT` 替代
2. **最后一步优先使用 `UPSERT INTO`**Kudu 核心优势:主键存在则更新,不存在则插入)
3. 需要全量刷新时:先 `DELETE FROM``INSERT INTO`
4. 支持 `UPDATE``DELETE`Kudu 表独有)
5. Kudu 表必须有 `PRIMARY KEY`,主键列不能为 NULL
6. `CONCAT` 只接受 2 个参数,多参数用 `CONCAT_WS`
7. 不支持 `collect_list` / `collect_set`,用 `GROUP_CONCAT` 替代
8. 近似去重用 `NDV()` 函数
9. 时间范围筛选:
```sql
-- 日账期过滤
WHERE stat_date = '${day_id}'
-- 日期范围
WHERE stat_date >= DAYS_SUB(TO_DATE('${day_id}'), 30)
AND stat_date < '${day_id}'
```
### SQL 脚本结构
```sql
-- =====================================================================
-- @SqlName: kudu-D-SQL-{表名}
-- @Engine: kudu
-- ...(头注释)
-- =====================================================================
-- Step01: {步骤描述}
DROP TABLE IF EXISTS ${db_tmp_env}.tmp_xxx_01;
CREATE TABLE ${db_tmp_env}.tmp_xxx_01 AS
SELECT ...;
-- Step02: {步骤描述}
DROP TABLE IF EXISTS ${db_tmp_env}.tmp_xxx_02;
CREATE TABLE ${db_tmp_env}.tmp_xxx_02 AS
SELECT ...;
-- 最后一步UPSERT 写入 Kudu 目标表
UPSERT INTO ${db_eda_env}.target_table
SELECT ...;
```

View File

@@ -0,0 +1,414 @@
# Spark SQL 语法要点
## 数据类型
| 类型 | 说明 | 示例 |
|------|------|------|
| STRING | 字符串 | name STRING |
| INT | 整数 | age INT |
| BIGINT | 大整数 | id BIGINT |
| DOUBLE | 浮点数 | price DOUBLE |
| DECIMAL(p,s) | 定点数 | amount DECIMAL(18,2) |
| BOOLEAN | 布尔 | active BOOLEAN |
| DATE | 日期 | birth_date DATE |
| TIMESTAMP | 时间戳 | created_at TIMESTAMP |
| ARRAY<type> | 数组 | tags ARRAY<STRING> |
| MAP<key,value> | 映射 | props MAP<STRING,STRING> |
| STRUCT<field:type,...> | 结构体 | user STRUCT<id:INT,name:STRING> |
---
## 时间函数
```sql
-- 当前时间
current_date()
current_timestamp()
now()
-- 格式转换
date_format(date_col, 'yyyy-MM-dd')
date_format(timestamp_col, 'yyyy-MM-dd HH:mm:ss')
to_date(string_col, 'yyyy-MM-dd')
to_timestamp(string_col, 'yyyy-MM-dd HH:mm:ss')
-- 日期计算
date_add(date_col, 7) -- 加7天
date_sub(date_col, 7) -- 减7天
add_months(date_col, 3) -- 加3个月
datediff(end_date, start_date) -- 日期差(天数)
-- 日期提取
year(date_col)
month(date_col)
day(date_col)
dayofweek(date_col)
hour(timestamp_col)
minute(timestamp_col)
second(timestamp_col)
-- 季度、周
quarter(date_col) -- 季度 (1-4)
weekofyear(date_col) -- 年中第几周
-- Unix 时间戳
unix_timestamp(date_col) -- 转 Unix 时间戳
from_unixtime(timestamp) -- Unix 时间戳转时间字符串
```
---
## 字符串函数
```sql
-- 常用函数
concat(str1, str2, ...) -- 字符串拼接
concat_ws('-', str1, str2, ...) -- 用分隔符拼接
lower(str) -- 转小写
upper(str) -- 转大写
trim(str) -- 去两端空格
ltrim(str) -- 去左空格
rtrim(str) -- 去右空格
length(str) -- 字符串长度
substring(str, pos, len) -- 截取字符串
left(str, len) -- 取左边len个字符
right(str, len) -- 取右边len个字符
reverse(str) -- 反转字符串
repeat(str, n) -- 重复n次
space(n) -- 生成n个空格
-- 查找与替换
instr(str, substr) -- 查找子串位置
locate(substr, str, pos) -- 从pos位置查找
replace(str, old, new) -- 替换
regexp_extract(str, pattern, idx) -- 正则提取
regexp_replace(str, pattern, replacement) -- 正则替换
-- 分割
split(str, delimiter) -- 分割成数组
split_part(str, delimiter, idx) -- 取分割后的第idx部分
-- 其他
initcap(str) -- 首字母大写
lpad(str, len, pad) -- 左填充
rpad(str, len, pad) -- 右填充
levenshtein(str1, str2) -- 编辑距离
```
---
## 条件表达式
```sql
-- CASE WHEN
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE default_result
END
-- CASE 字段匹配
CASE field
WHEN value1 THEN result1
WHEN value2 THEN result2
ELSE default_result
END
-- COALESCE取第一个非空值
COALESCE(col1, col2, default_value)
-- NULLIF相等返回NULL
NULLIF(col1, col2)
-- IF简单条件
IF(condition, true_value, false_value)
-- NVL空值替换
NVL(col, default_value)
```
---
## 聚合函数
```sql
-- 基础聚合
COUNT(*) -- 计数含NULL行
COUNT(col) -- 计数不含NULL
COUNT(DISTINCT col) -- 去重计数
SUM(col) -- 求和
AVG(col) -- 平均值
MIN(col) -- 最小值
MAX(col) -- 最大值
-- 集合聚合
collect_list(col) -- 返回数组(不去重)
collect_set(col) -- 返回数组(去重)
-- 统计函数
variance(col) -- 方差
var_pop(col) -- 总体方差
var_samp(col) -- 样本方差
stddev(col) -- 标准差
stddev_pop(col) -- 总体标准差
stddev_samp(col) -- 样本标准差
-- 近似函数
approx_count_distinct(col) -- 近似去重计数(大数据量优化)
-- 其他
first(col) -- 第一个值
last(col) -- 最后一个值
```
---
## 数学函数
```sql
-- 基础运算
abs(col) -- 绝对值
round(col, digits) -- 四舍五入
ceil(col) -- 向上取整
floor(col) -- 向下取整
sign(col) -- 符号 (-1, 0, 1)
-- 指数与对数
exp(col) -- e的指数
log(col) -- 自然对数
log10(col) -- 10为底对数
log2(col) -- 2为底对数
pow(col, n) -- 幂运算
sqrt(col) -- 平方根
-- 三角函数
sin(col), cos(col), tan(col)
asin(col), acos(col), atan(col)
-- 随机数
rand() -- 随机数 (0-1)
rand(seed) -- 指定种子随机数
-- 其他
cbrt(col) -- 立方根
hex(col) -- 转16进制
unhex(col) -- 16进制转字符串
```
---
## 数组函数
```sql
-- 创建数组
array(val1, val2, ...) -- 创建数组
-- 访问
array_contains(arr, val) -- 判断是否包含
element_at(arr, idx) -- 取元素idx从1开始
arr[idx] -- 取元素idx从0开始
-- 操作
size(arr) -- 数组长度
array_join(arr, delimiter) -- 数组转字符串
concat(arr1, arr2) -- 数组拼接
-- 展开
explode(arr) -- 展开数组为多行
posexplode(arr) -- 展开数组(带位置索引)
-- 排序与去重
sort_array(arr) -- 排序
array_distinct(arr) -- 去重
array_remove(arr, val) -- 移除元素
array_union(arr1, arr2) -- 并集
array_intersect(arr1, arr2) -- 交集
array_except(arr1, arr2) -- 差集
```
---
## Map 函数
```sql
-- 创建 Map
map(key1, val1, key2, val2, ...) -- 创建 Map
str_to_map(str, delim1, delim2) -- 字符串转 Map
-- 访问
map_contains(map, key) -- 判断是否包含key
element_at(map, key) -- 取值
map[key] -- 取值
map_keys(map) -- 取所有key返回数组
map_values(map) -- 取所有value返回数组
-- 操作
size(map) -- Map大小
map_concat(map1, map2) -- Map合并
```
---
## JSON 函数
```sql
-- 解析
get_json_object(json_str, path) -- 提取JSON字段
json_tuple(json_str, field1, ...) -- 提取多个字段
-- 转换
from_json(json_str, schema) -- JSON转结构体
to_json(struct_col) -- 结构体转JSON
-- Schema 定义示例
from_json('{"name":"张三","age":25}', 'name STRING, age INT')
```
---
## 分区表操作
```sql
-- 创建分区表
CREATE TABLE target_table (
id BIGINT,
name STRING,
amount DECIMAL(18,2)
)
PARTITIONED BY (day_id STRING)
STORED AS PARQUET;
-- 写入指定分区
INSERT OVERWRITE TABLE target_table
PARTITION (day_id = '${day_id}')
SELECT id, name, amount
FROM source_table
WHERE ...
-- 动态分区写入
INSERT OVERWRITE TABLE target_table
PARTITION (day_id)
SELECT id, name, amount, day_id
FROM source_table;
-- 查看分区
SHOW PARTITIONS target_table;
```
---
## 临时表与视图
```sql
-- 创建临时表
CREATE TEMPORARY TABLE tmp_table AS
SELECT ...
-- 创建临时视图
CREATE TEMPORARY VIEW tmp_view AS
SELECT ...
-- 全局临时视图跨Session
CREATE GLOBAL TEMPORARY VIEW global_view AS
SELECT ...
-- 删除
DROP TABLE IF EXISTS tmp_table;
DROP VIEW IF EXISTS tmp_view;
```
---
## MERGE INTO更新插入
```sql
-- MERGE INTO 语法
MERGE INTO target_table t
USING source_table s
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET t.name = s.name, t.amount = s.amount
WHEN NOT MATCHED THEN INSERT (id, name, amount) VALUES (s.id, s.name, s.amount)
-- 仅更新
MERGE INTO target_table t
USING source_table s
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET *
-- 仅插入
MERGE INTO target_table t
USING source_table s
ON t.id = s.id
WHEN NOT MATCHED THEN INSERT *
```
---
## Spark SQL 不支持的特性
| PostgreSQL 特性 | Spark SQL | 替代方案 |
|------------------|-----------|----------|
| CREATE INDEX | ❌ 不支持 | 依赖存储格式优化Parquet/ORC |
| CREATE TRIGGER | ❌ 不支持 | 使用程序逻辑处理 |
| FOREIGN KEY 约束 | ❌ 不强制 | 数据关联靠 JOIN 保证 |
| CHECK 约束 | ❌ 不支持 | 使用过滤条件 |
| ON CONFLICT (UPSERT) | 使用 MERGE INTO | - |
| WITH RECURSIVE | Spark 3.x+ 支持 | 或用程序迭代 |
| 物化视图 | ❌ 不支持 | 使用缓存或临时表 |
| 存储过程 | ❌ 不支持 | 使用外部程序 |
| FOR UPDATE 锁 | ❌ 不支持 | 无行级锁概念 |
---
## SQL 生成规则
### 通用规则(所有引擎统一)
1. **禁止使用 CTE (WITH 子句)**,每个主要逻辑步骤必须物化为临时表
2. **先 DROP 再 CREATE**`DROP TABLE IF EXISTS ...; CREATE TABLE ... AS SELECT ...;`
3. **禁止 `SELECT *`**,必须明确列出所有字段
4. 多表查询时所有表必须使用简短别名
5. 每个步骤前添加注释说明
6. **谓词下推**过滤条件前置JOIN 时在 WHERE 中一并添加过滤
7. 临时表命名:`${db_tmp_env}.tmp_{业务简称}_{步骤序号}`
8. 目标表命名:`${db_eda_env}.{目标表名}`
### Spark 特有规则
1. 使用 `INSERT OVERWRITE TABLE` 写入目标表
2. 分区表必须指定分区:`PARTITION (day_id = '${day_id}')`
3. 最后一步写入目标表,中间步骤物化临时表
4. 日期函数:`date_format()`, `to_date()`, `date_add()`, `add_months()`
5. 时间范围筛选:
```sql
-- 日账期过滤
WHERE day_id = '${day_id}'
-- 最近N个月月份格式 yyyyMM
WHERE month_id >= date_format(add_months(to_date('${month_id}', 'yyyyMM'), -N), 'yyyyMM')
AND month_id < '${month_id}'
```
### SQL 脚本结构
```sql
-- =====================================================================
-- @SqlName: spark-D-SQL-{表名}
-- @Engine: spark
-- ...(头注释)
-- =====================================================================
-- Step01: {步骤描述}
DROP TABLE IF EXISTS ${db_tmp_env}.tmp_xxx_01;
CREATE TABLE ${db_tmp_env}.tmp_xxx_01 AS
SELECT ...;
-- Step02: {步骤描述}
DROP TABLE IF EXISTS ${db_tmp_env}.tmp_xxx_02;
CREATE TABLE ${db_tmp_env}.tmp_xxx_02 AS
SELECT ...;
-- 最后一步:写入目标表
INSERT OVERWRITE TABLE ${db_eda_env}.target_table
PARTITION (day_id = '${day_id}')
SELECT ...;
```

View File

@@ -0,0 +1,306 @@
# 窗口函数速查
## 基本语法
```sql
() OVER (
PARTITION BY -- 可选:分组
ORDER BY -- 可选:排序
ROWS/RANGE -- 可选:窗口范围
)
```
---
## 排序函数
| 函数 | 说明 | 特点 | 适用场景 |
|------|------|------|----------|
| ROW_NUMBER() | 连续排名 | 不跳号,相同值不同排名 | 每组取前N条、去重 |
| RANK() | 排名 | 跳号,相同值相同排名 | 成绩排名 |
| DENSE_RANK() | 紧密排名 | 不跳号,相同值相同排名 | 连续名次 |
| NTILE(n) | 分桶 | 分成n组 | 数据分片、抽样 |
### ROW_NUMBER 示例
```sql
-- 每个部门薪资最高的员工(取第一名)
SELECT *
FROM (
SELECT
name,
department,
salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE rn = 1
-- 去重:每个用户取最新订单
SELECT *
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders
) t
WHERE rn = 1
```
### RANK 与 DENSE_RANK 示例
```sql
-- 成绩排名(跳号)
SELECT
name,
score,
RANK() OVER (ORDER BY score DESC) AS rank, -- 1,2,2,4,5...
DENSE_RANK() OVER (ORDER BY score DESC) AS dense -- 1,2,2,3,4...
FROM students
```
### NTILE 示例
```sql
-- 将用户分成4组业绩分位
SELECT
name,
sales,
NTILE(4) OVER (ORDER BY sales DESC) AS quartile -- 1(最高)到4(最低)
FROM sales_data
```
---
## 聚合函数(窗口内)
| 函数 | 说明 |
|------|------|
| SUM(col) OVER | 累计求和 |
| AVG(col) OVER | 累计平均 |
| COUNT(col) OVER | 窗口内计数 |
| MAX(col) OVER | 窗口内最大值 |
| MIN(col) OVER | 窗口内最小值 |
### 累计求和示例
```sql
-- 累计销售额(从开始到当前)
SELECT
date,
amount,
SUM(amount) OVER (ORDER BY date) AS cumulative_amount
FROM daily_sales
-- 分组累计(每个部门累计)
SELECT
date,
department,
amount,
SUM(amount) OVER (PARTITION BY department ORDER BY date) AS dept_cumulative
FROM sales_data
```
### 移动平均示例
```sql
-- 7天移动平均
SELECT
date,
amount,
AVG(amount) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7d
FROM daily_sales
```
---
## 偏移函数
| 函数 | 说明 | 适用场景 |
|------|------|----------|
| LAG(col, n) | 取前n行的值 | 环比、查看历史值 |
| LAG(col, n, default) | 取前n行无值返回default | 防止NULL |
| LEAD(col, n) | 取后n行的值 | 查看未来值 |
| LEAD(col, n, default) | 取后n行无值返回default | 防止NULL |
| FIRST_VALUE(col) | 窗口第一个值 | 组内首个值 |
| LAST_VALUE(col) | 窗口最后一个值 | 组内末尾值 |
### LAG 示例(环比计算)
```sql
-- 日环比增长
SELECT
date,
amount,
LAG(amount, 1) OVER (ORDER BY date) AS prev_day,
amount - LAG(amount, 1) OVER (ORDER BY date) AS daily_growth,
ROUND((amount - LAG(amount, 1) OVER (ORDER BY date))
/ LAG(amount, 1) OVER (ORDER BY date) * 100, 2) AS growth_rate_pct
FROM daily_sales
-- 月度同比(取去年同期)
SELECT
month,
revenue,
LAG(revenue, 12) OVER (ORDER BY month) AS prev_year_revenue,
ROUND((revenue - LAG(revenue, 12) OVER (ORDER BY month))
/ LAG(revenue, 12) OVER (ORDER BY month) * 100, 2) AS yoy_growth_pct
FROM monthly_revenue
```
### LEAD 示例
```sql
-- 查看下一行数据
SELECT
date,
amount,
LEAD(amount, 1) OVER (ORDER BY date) AS next_day_amount
FROM daily_sales
```
### FIRST_VALUE / LAST_VALUE 示例
```sql
-- 每个部门薪资最高和最低的人
SELECT
name,
department,
salary,
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary DESC) AS highest_paid,
LAST_VALUE(name) OVER (
PARTITION BY department
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS lowest_paid
FROM employees
```
**注意**LAST_VALUE 默认窗口是 `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`,需要显式指定全窗口才能取到最后值。
---
## 窗口范围定义
### ROWS基于行数
```sql
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW -- 前3行到当前行
ROWS BETWEEN 6 PRECEDING AND 1 FOLLOWING -- 前6行到后1行
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -- 从开始到当前
ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING -- 当前行到后3行
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING -- 全窗口
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING -- 当前行及前后各1行共3行
```
### RANGE基于值范围
```sql
RANGE BETWEEN 100 PRECEDING AND CURRENT ROW -- 值差100以内
RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW -- 7天内
```
**ROWS vs RANGE**
- ROWS固定行数不受值影响
- RANGE根据排序字段的值计算范围
---
## 实战案例
### 案例1分组取Top N
```sql
-- 每个部门薪资前3名
SELECT *
FROM (
SELECT
name,
department,
salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE rn <= 3
```
### 案例2连续问题判断
```sql
-- 判断是否连续增长连续3天增长
SELECT
date,
amount,
CASE
WHEN amount > LAG(amount, 1) OVER (ORDER BY date)
AND LAG(amount, 1) OVER (ORDER BY date) > LAG(amount, 2) OVER (ORDER BY date)
THEN '连续增长'
ELSE '非连续增长'
END AS trend
FROM daily_sales
```
### 案例3组内占比
```sql
-- 每个部门各员工薪资占比
SELECT
name,
department,
salary,
ROUND(salary / SUM(salary) OVER (PARTITION BY department) * 100, 2) AS salary_pct
FROM employees
```
### 案例4累计百分比帕累托分析
```sql
-- 80/20分析哪些客户贡献了80%销售额
SELECT
customer_id,
sales_amount,
ROUND(SUM(sales_amount) OVER (ORDER BY sales_amount DESC)
/ SUM(sales_amount) OVER () * 100, 2) AS cumulative_pct
FROM customer_sales
ORDER BY sales_amount DESC
```
### 案例5缺失值填充
```sql
-- 用前一个有效值填充NULL
SELECT
date,
amount,
LAST_VALUE(amount IGNORE NULLS) OVER (ORDER BY date) AS filled_amount
FROM sales_data
```
---
## 性能优化建议
1. **减少 PARTITION BY 分组数量**:分组越多,计算越慢
2. **合理使用窗口范围**:避免全窗口扫描
3. **先过滤再窗口**WHERE 条件前置,减少数据量
4. **避免嵌套窗口函数**:多次调用会重复计算
```sql
-- 推荐:先过滤
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
WHERE hire_date >= '2024-01-01' -- 先过滤,减少数据量
-- 不推荐:先窗口再过滤(窗口函数在全部数据上执行)
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (...) AS rn
FROM employees
) t
WHERE hire_date >= '2024-01-01'
```