SQL的存在意义
Ianus Inferus
2026-05-24
最近看到一些关于SQL难以被vibe的说法,产生了一些想法。
在大学学习SQL的时候,就觉得SQL是一个很不好的语言。
- 执行顺序和书写顺序不一致,select最后执行却在最前写,不能像lambda表达式一样顺序书写
- group by之后无法得到列表的列表,必须进行映射
- 多次引用同一个查询时,无法通过变量引用,只能重写一遍
- 对于需要事务的事情,无法将计算轻易地放到数据库中执行,需要使用不通用的存储过程,但某些运算难以实现
- 声明式,执行引擎优化后实际执行的方法不可控
- 查询一个集合的多个方面,可能需要分布到多个事务中进行,影响数据一致性
也有一些对于SQL的改进想法,例如关系代数算子,GraphQuery,但并非非常成功,主流数据库仍然没有放弃SQL语言。
但是,对于异构计算来说,GPU上的计算现在非常成功,尤其是像CUDA、Pytorch这样的执行引擎。
实际上,数据库上的计算和显卡上的计算没有区别,也可以用类似的思想来实现。
将计算过程写成一个有向无环图(DAG),每个结点表示一个算子,类似于编译过程中语义阶段产生的每个函数的计算步骤构成的有向无环图一样。算子包括读取、写入、映射、笛卡尔积等不同的操作。再将图序列化传给数据库服务器,由服务器进行JIT,再执行。需要高性能的情况下,可将图进行AOT。
以下为一个示例图,说明如何通过DAG来表示一个计算过程。
从一个用户登录记录表计算用户总数,以及7日活跃数、30日活跃数,并且提取登录次数最多的前20名用户。
digraph G {
// 基础设置
rankdir=HV;
node [shape=box, style="filled, rounded", fontname="Arial"];
compound=true;
// 数据源节点
subgraph cluster_sources {
label = "Data Sources";
color = lightgrey;
login_records [label="Source: login_records", fillcolor="#e1f5fe"];
}
// 中间变量/算子
logins_map [label="Map: {user_id, time}", fillcolor="#fff9c4"];
// 支流 A: 活跃统计
stats_agg [label="Aggregate:\l- count_distinct(all)\l- filter(7d).count()\l- filter(30d).count()", fillcolor="#c8e6c9"];
// 支流 B: Top 20
group_by_user [label="Group By: user_id", fillcolor="#fff9c4"];
count_agg [label="Aggregate: count()", fillcolor="#c8e6c9"];
sort_node [label="Sort: desc(count)", fillcolor="#ffccbc"];
take_node [label="Take: 20", fillcolor="#ffccbc"];
// 最终汇聚
result_sink [label="Result Sink\n(Summary + Details)", fillcolor="#d1c4e9", shape=penta];
// 逻辑连接
login_records -> logins_map;
// 分支点
logins_map -> stats_agg [label="Branch A"];
logins_map -> group_by_user [label="Branch B"];
// 支流 B 的后续
group_by_user -> count_agg;
count_agg -> sort_node;
sort_node -> take_node;
// 汇聚到结果
stats_agg -> result_sink;
take_node -> result_sink;
}
代码
// 1. 定义数据源(节点)
val raw_logins = Source("login_records")
// 2. 提取公共中间变量(分流点)
val logins = raw_logins.map(r -> {user_id: r.user_id, time: r.login_time})
// 3. 支流 A:计算多维活跃指标(利用 Lambda 灵活处理)
val stats = logins.aggregate(
total_users = count_distinct(u -> u.user_id),
active_7d = count_distinct(u -> u.user_id).filter(u -> u.time > now - 7d),
active_30d = count_distinct(u -> u.user_id).filter(u -> u.time > now - 30d)
)
// 4. 支流 B:计算排行榜(逻辑并行,互不干扰)
val top_20 = logins
.group_by(u -> u.user_id)
.aggregate(count = count())
.sort_by(desc(u -> u.count))
.take(20)
// 5. 汇聚结果(Sink)
return Result(summary = stats, details = top_20)
总结:数据库也应使用有向无环算子图的形式来进行计算。