第二章 SQL基础回顾:数据库、表、select查询归纳总结

说实话,每次带新人做团伙识别项目,总有人上来就写千行大SQL,结果连表结构都没看全。我自己的习惯是,不管多复杂的图谱特征衍生,先回去把SQL基础捋一遍。这一章我们就快速过一下数据库、表、SELECT这三样东西,不是给你讲语法手册,而是把项目里最常用、最容易踩坑的点拎出来。

核心提要:本章会覆盖关系数据库基本概念、表设计原则、SELECT查询执行顺序、常见函数以及多表关联。这些都是后续图谱特征衍生的地基。

2.1 数据库与表——不只是“存数据”

数据库是什么?说白了就是一个有组织的数据容器。在团伙识别场景里,我们通常用的就是关系型数据库(PostgreSQL、MySQL 居多)。表就是二维结构,行叫记录,列叫字段。

我记得有一次项目,合作方把电话号码存在同一个字段里,用逗号分隔。我问为什么不拆成单独的表?对方说“这样查询快”。嗯,你想想看,团伙识别要频繁做聚类、关联,这种反范式设计直接导致后续SQL写得像噩梦。所以我的建议是:遵循至少第三范式,针对分析场景合理冗余,但不要为了“省事”把多值塞进一个字段。

术语解释团伙识别中的例子
数据库(Database)数据集合的容器,对应一个项目或业务域gang_analysis 库
表(Table)具有相同结构的数据行集合call_records 表,存放通话记录
字段(Column)表的属性列caller_id, callee_id, duration
记录(Row)表的一行数据某一次通话的具体数据

建表的时候,主键、外键、索引是怎么设计的,直接影响后面图谱特征衍生性能。我见过有人把全表扫描当成常态,结果亿级表跑特征跑了三天。所以,花10分钟做好表设计,比后面花10小时调优划算得多。

避坑指南:我曾经在项目中直接用 SELECT * 从几十亿行的日志表里捞数据,结果把生产库搞挂了。记住:查询时永远只选你需要的列,不仅快,而且安全。

2.2 SELECT 查询——从数据到信息的桥梁

SELECT 语句是你与数据对话的工具。多数人只记得基本语法,但核心是理解它的执行顺序。很多人写得很长,结果数据量和预期完全不一样,就是没搞清楚 SQL 到底先干哪一步。

我习惯用这张顺序表来帮自己和团队记牢:

步骤关键字作用
1FROM + JOIN确定数据源,做表关联
2WHERE行级过滤,删掉不满足条件的记录
3GROUP BY分组,聚合计算的基础
4HAVING分组后过滤,对聚合结果做筛选
5SELECT 子句投影列,计算表达式
6DISTINCT去重(实际上在SELECT之后执行)
7ORDER BY排序
8LIMIT / OFFSET限制返回行数

注意:你写的 HAVING 条件里引用的别名,必须是在 SELECT 中已经定义过的,因为 HAVING 执行在 SELECT 投影之后;而 WHERE 里不能用聚合后的别名,因为那时还没聚合。这个坑,我见过不下五次。

来看一个团伙识别的简单例子:统计每个号码作为主叫的通话次数,只保留通话次数大于 5 的号码。

SELECT 
    caller_id,
    COUNT(*) AS call_count
FROM call_records
WHERE callee_id IS NOT NULL
GROUP BY caller_id
HAVING COUNT(*) > 5
ORDER BY call_count DESC;

这个查询里,WHERE 先过滤掉无效被叫,然后按主叫分组,HAVING 只保留高频主叫,最后降序排列。每一步都按上面的顺序来的,心里有数,就不会犯「在 WHERE 里用聚合函数」之类的错。

小技巧:复杂查询可以先写 FROM + WHERE 测试行数,确认过滤逻辑正确,再加 GROUP BY 和聚合。我每次至少做两次这种分段验证。

2.3 常用函数与聚合——把数据转换成特征

团伙识别特征的源头,基本就是各种聚合函数、窗口函数以及字符串/日期函数。这里我只挑最常用的几个列个表,后面章节会深入讲它们的组合用法。

函数类别函数示例用途举例(团伙场景)
聚合函数COUNT(), SUM(), AVG(), MAX(), MIN()统计每个节点出现的次数、金额总和、平均通话时长
字符串函数CONCAT(), SUBSTRING(), REPLACE()拼接识别号、截取地区编码、清洗脏数据
日期函数DATE_TRUNC(), EXTRACT(), DATEDIFF()按天/周聚合行为、计算周期
窗口函数ROW_NUMBER(), RANK(), LAG(), LEAD()排序、前后事件间隔、团伙内排名

注意:COUNT(*) 和 COUNT(1) 没区别,但 COUNT(column) 会忽略 NULL。写聚合前一定要确认字段里有没有 NULL,不然结果少一行都不知道。

2.4 多表关联——图谱的骨架

团伙识别本质就是发现实体间的关联。多表关联用得最多的就是 INNER JOIN 和 LEFT JOIN。我见过一个项目,用笛卡尔积 CROSS JOIN 做关联,结果数据直接指数级爆炸。所以,关联必须有明确的条件。

举个例子,把通话记录表和用户基本信息表关联,捞取主叫的注册地区:

SELECT 
    c.caller_id,
    u.region,
    COUNT(*) AS call_cnt
FROM call_records c
INNER JOIN user_info u ON c.caller_id = u.user_id
WHERE c.call_date >= '2024-01-01'
GROUP BY c.caller_id, u.region;

这个查询里,关联条件在 ON 里,过滤在 WHERE 里,分组在 GROUP BY 里,逻辑清晰。如果先 WHERE 再 JOIN,性能通常比先 JOIN 再 WHERE 差(大多数优化器会智能调整,但写清楚更不容易出错)。

小结:多表关联最核心的是搞清楚驱动表和被驱动表,以及连接键上的索引。没有索引的大表 JOIN,就是灾难。

SQL 查询执行流程(团伙识别场景) FROM + JOIN(表关联) WHERE(行级过滤) GROUP BY(分组) HAVING(分组后过滤) SELECT + ORDER BY + LIMIT (投影、排序、限制) 最终输出:图谱特征、统计结果等

这张流程图把 SELECT 语句里每一步的先后顺序标清楚了。后面我们做图谱特征衍生时,每一步想做什么、数据在经过哪一步后变成什么样子,心里就能有个谱。

我的习惯:在写任何复杂查询前,先在草稿纸上按这个流程画一遍。看起来笨,但能省掉至少一半的 debug 时间。