DD 数据开发学习站DATA ENGINEERING
模块 10 · 附录

命令与 SQL 速查

SQL、Hive、Spark、Linux、Git、PySpark、Kafka 常用命令速查。

难度 · 入门 约 20 分钟 选学 练习 5 项 计划内
建议先学:求职准备与投递
本页目录 · 24 节

本章目标:写 SQL、PySpark、Linux 命令或准备面试时,能在 30 秒内查到正确写法,并知道 Hive/Spark 与本地 DuckDB 的差异。 前置知识:无,配合各章使用。 使用方式:打印出来放手边,或面试前 20 分钟快速过一遍。

1. 怎么用这张速查表

不要从头背,按场景查:写练习卡住时抄最小示例再改字段;报错时对照常见坑和方言差异;面试前遮住右列自测。速查表只解决“怎么写”,口径和建模看数仓指标口径。

2. SQL 速查

2.1 查询骨架与执行顺序

SELECT   columns
FROM     table
WHERE    row_conditions
GROUP BY columns
HAVING   group_conditions
ORDER BY columns
LIMIT    n;

书写顺序是上面的顺序,逻辑执行顺序大致是:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT。所以 WHERE 里不能直接用聚合函数,SELECT 的别名通常也不能在 WHERE 里用。

2.2 条件、空值与去重

需求 写法
等值/范围/集合 status = 'delivered'、age BETWEEN 18 AND 30、city IN ('北京','上海')
模糊匹配 product_name LIKE '%电脑%'
多条件 status = 'delivered' AND price > 0
空值判断 customer_id IS NULL、IS NOT NULL
空值替换 COALESCE(customer_level, '未知')
空值转条件 NULLIF(amount, 0)
去重 SELECT DISTINCT city FROM customers
条件计数 SUM(CASE WHEN status = 'canceled' THEN 1 ELSE 0 END)

NULL = NULL 结果是未知,不是真。判断空值必须用 IS NULL。COUNT(column) 不统计空值,COUNT(*) 统计行数。

2.3 聚合与分组

需求 写法
计数/求和/平均 COUNT(DISTINCT customer_id)、SUM(price * quantity)、AVG(amount)
最值 MIN(order_date)、MAX(order_date)
分组 GROUP BY category
分组后过滤 HAVING COUNT(*) > 100
条件聚合 SUM(CASE WHEN status = 'delivered' THEN 1 ELSE 0 END)
多行合并 STRING_AGG(city, ',')(Hive 用 collect_list)

WHERE 过滤行,HAVING 过滤分组。SELECT 中非聚合列必须出现在 GROUP BY 里。

2.4 JOIN

类型 作用 保留哪边
INNER JOIN 两边都匹配才保留 交集
LEFT JOIN 左表全保留 左表 + 右表匹配
RIGHT JOIN 右表全保留 右表 + 左表匹配
FULL OUTER JOIN 两边都保留 并集
CROSS JOIN 笛卡尔积 全部组合
SELECT o.order_id, c.province, SUM(i.price * i.quantity) AS gmv
FROM orders o
JOIN order_items i ON o.order_id = i.order_id
LEFT JOIN customers c ON o.customer_id = c.customer_id
WHERE o.status NOT IN ('canceled', 'unavailable')
GROUP BY o.order_id, c.province;

关联前确认右表关联键唯一,否则行数会膨胀。LEFT JOIN 后对右表字段做 WHERE 过滤,可能把左连接变成内连接;需要保留左表时把条件下移到 ON。

2.5 窗口函数

函数 作用
ROW_NUMBER() OVER (PARTITION BY x ORDER BY y) 组内排名,不重复
RANK() / DENSE_RANK() 排名,允许并列;DENSE 不留空位
SUM(x) OVER (PARTITION BY g ORDER BY d) 累计求和
SUM(x) OVER (PARTITION BY g) 组内总和,不排序
LAG(x, 1) OVER (...) / LEAD(...) 取上一行/下一行
AVG(x) OVER (ORDER BY d ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) 7 日移动平均
NTILE(4) OVER (ORDER BY x) 分成 4 桶

窗口函数写在 SELECT 里,不能直接写在 WHERE。要过滤排名,外层再包一层子查询或 CTE。

2.6 日期与字符串

需求 DuckDB 写法 Hive/Spark 写法
当前日期 CURRENT_DATE current_date(Spark 也可加括号)
截到月 DATE_TRUNC('month', d) trunc(d, 'MM')
加一天 d + INTERVAL 1 DAY date_add(d, 1)
减 7 天 d - INTERVAL 7 DAY date_sub(d, 7)
日期差 DATE_DIFF('day', start, end) datediff(end, start)
格式化 strftime(d, '%Y-%m') date_format(d, 'yyyy-MM')
字符串转日期 CAST(s AS DATE) to_date(s)
拼接 concat('a', 'b') concat('a', 'b')(也支持双竖线)
截取 SUBSTRING(s, 1, 3) substr(s, 1, 3)
大小写 LOWER(s)、UPPER(s) 同左
去空格 TRIM(s) trim(s)
替换 REPLACE(s, 'a', 'b') regexp_replace(s, 'a', 'b')

日期差最容易错:Hive/Spark 的 datediff 是“结束日期在前、开始日期在后”,DuckDB 的 date_diff 把单位放在第一个参数。

2.7 CTE 与 UNION

CTE 写法:WITH t AS (SELECT ...) SELECT * FROM t;,适合把清洗步骤拆开。UNION 去重,UNION ALL 不去重;能用 UNION ALL 就不加 UNION,少一次排序去重。完整例子见第 7 节。

3. Hive / Spark SQL 差异速查

3.1 课程统一版本

组件 版本 说明
Java 17 Spark 3.5 官方支持
Spark / PySpark 3.5.5 pip install pyspark==3.5.5,Python 3.8+
Hadoop 3.3.6 只做 HDFS/YARN 概念演示
Hive 3.1.3 与 Hadoop 3.3 搭配
Kafka 3.7 Docker 单机 KRaft 模式
Flink / PyFlink 1.18 pip install apache-flink==1.18.1
Airflow 2.10 3.x 的 UI、CLI 和 provider 包有变化
ClickHouse 24.3 Docker 镜像 clickhouse/clickhouse-server:24.3

正文和本章引用同一组版本。升级时先改这张表,再检查示例命令。

3.2 方言差异

场景 DuckDB Hive Spark SQL
查 CSV read_csv_auto('f.csv') 先建外部表 spark.read.csv(...)
建表 CREATE TABLE ... CREATE TABLE ... STORED AS ORC 同 Hive,支持 Delta/Iceberg
覆盖写 CREATE OR REPLACE TABLE INSERT OVERWRITE TABLE ... PARTITION 同 Hive
行拆多行 UNNEST(list) LATERAL VIEW explode(arr) t AS x explode(arr)
多行合并 list(col) collect_list(col) / collect_set(col) collect_list / collect_set
JSON 取值 json_extract_string(j, '$.k') get_json_object(j, '$.k') get_json_object / from_json
空值替换 COALESCE nvl / COALESCE coalesce / nvl
字符串拼接 concat(或双竖线) concat concat(或双竖线)
日期差 date_diff('day', a, b) datediff(b, a) datediff(b, a)
分区裁剪 无分区概念 查询带 WHERE dt = '...' 同 Hive
分桶 无 CLUSTERED BY ... INTO n BUCKETS 支持,较少用

Hive 的 INSERT OVERWRITE 覆盖写天然幂等,补数常靠它。Spark 默认是批处理引擎;cache() 用于重复使用的 DataFrame,explain() 看执行计划。

4. Linux 与 Git 速查

场景 命令
看目录/找内容 pwd、ls -lh、find data -name "*.csv"、grep -n "error" app.log
看头尾/统计 head -n 5 f、tail -f app.log、wc -l f
排序去重/按列处理 sort f 后接 uniq -c 和 sort -nr、awk '{print $3}' f、sed -n '1,3p' f
打包/磁盘/权限 tar -czf raw.tar.gz data/raw、du -sh data、df -h、chmod +x run.sh
管道/重定向 管道用竖线连接两条命令;cmd > out.txt 2>&1
Git 初始化/提交 git init -b main、git status、git add f、git commit -m "msg"、git log --oneline
Git 分支/远程 git switch -c feature/x、git merge feature/x、git push -u origin main、git pull --rebase
Git 撤销 git restore f、git restore --staged f、git revert <commit>

5. PySpark 常用 API

本课程统一 Java 17 + PySpark 3.5.5。

from pyspark.sql import SparkSession, functions as F

spark = SparkSession.builder.master("local[*]").appName("cheatsheet").getOrCreate()

orders = spark.read.csv("data/sample/orders_demo.csv", header=True, inferSchema=True)
orders.select("order_id", "status").filter(
    F.col("status") == "paid"
).groupBy("status").count().show()

orders.createOrReplaceTempView("orders")
spark.sql("SELECT status, COUNT(*) AS cnt FROM orders GROUP BY status").show()

orders.dropDuplicates(["order_id"]).orderBy("order_id").show(5)
orders.cache()
orders.explain()

orders.write.mode("overwrite").parquet("output/orders")
spark.stop()
需求 DataFrame API SQL
选列/改名 df.select("a")、df.withColumnRenamed("a", "b") SELECT a AS b
新增列 df.withColumn("amount", F.col("price") * F.col("quantity")) SELECT price * quantity AS amount
筛选 df.filter(F.col("status") == "delivered") WHERE status = 'delivered'
分组 df.groupBy("category").agg(F.sum("amount").alias("gmv")) GROUP BY category
关联 df.join(other, on="order_id", how="left") LEFT JOIN
去重 df.dropDuplicates(["order_id"]) SELECT DISTINCT
排序 df.orderBy(F.col("gmv").desc()) ORDER BY gmv DESC
写文件 df.write.mode("overwrite").parquet("output/x") INSERT OVERWRITE

窗口函数用 from pyspark.sql import Window,例如 F.row_number().over(Window.partitionBy("user_id").orderBy("event_date"))。

6.1 Kafka 概念

概念 一句话
topic 消息的分类,例如 order_events
partition topic 的分片,决定并行度和顺序范围
offset 每条消息在 partition 内的位置
producer / consumer 写入方 / 读取方
consumer group 同组内一个 partition 只由一个消费者处理
broker Kafka 服务节点
replication 副本数,保证节点挂了数据还在
acks=all 所有副本确认后才算写入成功,可靠性高、延迟大

顺序性只保证单 partition 内有序。同一订单的事件要用 order_id 做 key,让它们进同一个 partition。

6.2 Kafka 本地常用命令

docker run -d --name kafka -p 9092:9092 apache/kafka:3.7.0
docker exec -it kafka /opt/kafka/bin/kafka-topics.sh \
  --create --topic order_events --bootstrap-server localhost:9092
docker exec -it kafka /opt/kafka/bin/kafka-topics.sh \
  --list --bootstrap-server localhost:9092
docker exec -it kafka /opt/kafka/bin/kafka-console-producer.sh \
  --topic order_events --bootstrap-server localhost:9092
docker exec -it kafka /opt/kafka/bin/kafka-console-consumer.sh \
  --topic order_events --from-beginning --bootstrap-server localhost:9092

这是本地单机模拟,不是生产部署。生产环境要考虑副本、监控、磁盘和权限。

概念 一句话
事件时间 / 处理时间 数据发生的时间 / 系统收到的时间
水位线 Watermark 声明“晚于这个时间的数据不再等”
滚动窗口 窗口不重叠,例如每 5 秒
滑动窗口 窗口有重叠,例如每 5 秒统计最近 10 秒
会话窗口 按活跃间隔切分
状态 State 算子保存的中间结果
Checkpoint 定期快照,故障后恢复
精确一次 端到端不重不丢,靠两阶段提交 + 幂等写出

常用命令与入口:

目的 命令
安装 PyFlink python -m pip install apache-flink==1.18.1
运行 PyFlink 作业 python job.py(脚本内用本地执行环境)
进入 Flink SQL Client bin/sql-client.sh(已有 Flink 安装时)

本地练习用 PyFlink 的本地环境跑一个滚动窗口统计即可。装不上不影响 SQL 主线,先做到“讲得清”。

7. 使用示例:品类 GMV 排名

直接用 DuckDB 读取 data/sample/ 下的示例 CSV,不依赖任何项目库:

WITH valid_orders AS (
    SELECT order_id
    FROM read_csv_auto('data/sample/orders_demo.csv', header = true)
    WHERE status NOT IN ('canceled', 'unavailable')
)
SELECT i.category,
       ROUND(SUM(i.price + i.freight), 2) AS gmv
FROM read_csv_auto('data/sample/order_items_demo.csv', header = true) i
JOIN valid_orders o USING (order_id)
GROUP BY i.category
ORDER BY gmv DESC;

预期结果:数码 885.00、家居 375.00、服饰 249.00,合计 1,509.00。示例只有 9 个有效订单,所以这里不写 LIMIT 5。跑出来对不上时,先检查是否排除了 canceled 和 unavailable 两种状态。

8. 常见坑

坑 正确做法
用 = NULL 判断空值 用 IS NULL
WHERE 里用聚合函数 聚合条件放 HAVING
日期差参数顺序照搬 DuckDB Hive/Spark 是 datediff(结束, 开始)
字符串用双引号 SQL 字符串用单引号;双引号多为标识符
LEFT JOIN 后在 WHERE 过滤右表 条件移到 ON,否则退化成 INNER JOIN
关联键不唯一导致行数膨胀 关联前先 COUNT(*) 和 COUNT(DISTINCT key)
直接把 CSV 查询语法搬到 Hive Hive 要先建外部表,再查询
把本地 Kafka 命令当生产部署 明确说“本地模拟”,生产要副本和监控

9. 面试延伸

面试题:为什么窗口函数不能直接写在 WHERE 里?

是什么:窗口函数在 SELECT 阶段计算,而 WHERE 在它之前执行;执行到 WHERE 时窗口结果还不存在。

解决什么问题:要过滤排名或累计值,必须先用子查询/CTE 算出窗口列,再在外层 WHERE 过滤。理解执行顺序还能解释为什么 SELECT 别名不能在 WHERE 中使用。

我的实操边界:我在连续登录、品类排名、月度同比里用过 ROW_NUMBER、LAG、SUM OVER,并用 CTE 过滤排名;没有处理过超大分区下的窗口性能问题。

面试题:Kafka 如何保证同一订单的消息有序?

是什么:Kafka 只保证单 partition 内有序,不保证 topic 全局有序。

解决什么问题:生产消息时用 order_id 作为 key,Kafka 会把同一 key 的消息路由到同一 partition,消费者按 offset 顺序读取,就能保证该订单的事件有序。

我的实操边界:我在本地单机 Kafka 里跑过生产消费 demo,理解 key、partition、offset;没有在生产集群处理过 rebalance 和跨机房副本。

10. 本章验收

  • 能不看资料写出 SELECT … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT 的骨架和执行顺序。
  • 能写出 INNER/LEFT JOIN、窗口排名、日期截断、空值替换、去重的正确语法。
  • 能说出至少 5 处 Hive/Spark 与 DuckDB 的方言差异,尤其是日期差参数顺序。
  • 能查表完成一次 Linux 日志统计和一次 Git 分支合并,不需要从头搜教程。
  • 能在 30 秒内查到 PySpark 的读取、筛选、分组、关联、写出写法,并说清 Kafka/Flink 的核心概念。

打印建议:A4 双面,第 2–6 节放前两页,方言差异和常见坑放第三页。配合数仓指标口径使用。

本章任务0 / 5
本页目录