「2022 年什么会火?什么该学?本文正在参与“聊聊 2022 技术趋势”征文活动 」
从 Don Chamberlin 和 Ramond Boyce 发表“SEQUEL:一门结构化的英语查询语言”以来,关系型模型和 SQL 已经得到了广泛扩展,并被用在大量的技术中,如 OLTP、OLAP、对象数据库、对象关系型数据库,甚至是 NoSQL 数据库。SQL 也为非关系型数据库带去了设计灵感,比如用于对象数据库的 SQL、用于对象关系的 SQL、用于 XML 的 SQL、用于空间数据的 SQL、用于搜索的 SQL、用于 JSON 的 SQL、用于时序数据的 SQL、用于流的 SQL,等等。各种与数据打交道的 BI 工具也使用了 SQL。事实上,SQL 是最成功的第四代语言。
正如Don最近所说的,SQL是基于关系代数的基础,目的是通过提供一个类似于英语的查询语言来更简单地实现以下目标:
- 声明性的语言和流程(而不是程序性的)
- 使语言可组合以帮助轻松编写复杂的查询
- 和Edger F Codd开发的关系模型共同工作
本质上,结构化查询语言(SQL)用于检索数据或与关系数据库交互。 作为可以追溯到1970年代的标准,SQL是一种从关系数据库系统中获取信息的流行方法。 关系数据库以特定的结构设置-每个记录都有一系列键,这些键以一致的方式相互链接,并放置在以网格形式直观表示的“表”中。
编写SQL语言是为了梳理常规数据库中表的内容。 SQL被广泛用于商业和其他类型的数据库管理中。 它是在常规数据库上进行“操作”,更改表数据,检索数据或以其他方式处理现有数据集的默认工具。
大数据时代
大数据试图在数据仓库领域补足或替换关系型系统,但它们仍然使用了 SQL。Hive、Impala、drill、BigSQL 都使用了基于 SQL 的语言、优化器,并使用了与 SQL 相似的大规模并行处理。它们还时不时地增加新的 SQL 特性。SQL 中的数据存储格式、数据模型和查询处理的分离带来了一些非常重要的好处。在 SQL 诞生以来的 45 年当中,很多数据库来了又去。NoSQL 运动浪潮甚至在无意中暗示了 SQL 和 SQL 数据库即将死掉。但 SQL 阵营很坦然地面对这个问题,Don Chamberlin 最近表示:“当一门语言被广泛认可,以至于其他语言开始标榜自己不同于这一门语言时,说明这门语言一定表现得很好”。
选择SQL的原因
- 声明性:你只需要声明输出,查询引擎就会找出执行查询的最佳方式。优化器,特别是1979年Pat Selinger等人发明的基于成本的优化器,帮助持续地改进性能。这为每个新进入者提供了一个很高的标准。最近一篇关于Apache Hive的论文就是一个复杂性和完善涉及的例子为什么SQL如此成功?
- SQL不仅用于“查询”,还用于更新数据、执行事务。存储过程,UDF通过将过程语言与声明性SQL相结合来扩展访问范围。
- SQL具有可塑性。它已经多次标准化,每次都会添加一本功能齐全的书,一个充满语法的商店,以及一个充满关键词的词典。当然,并非所有的SQL都是相同的。即使是RDBMS上的传统SQL实现也不完全兼容,除非您小心地编写SQL使其兼容。通过所有这些,SQL的原始精神得以保留。SQL的一个进化的例子是SQL++。Don Chamberlin和Mike Carey教授讨论了支持复杂数据模型的需求,使用户和开发人员可以轻松访问JSON中的数据。Don写的书《SQL++ for SQL users:A Tutorial》介绍了SQL++的最新发展,SQL++这种语言是为灵活的JSON数据模型上的数据处理而设计的,它保持了与SQL的兼容。
- 就像它所借用的英语一样,SQL对新数据类型、访问方法和用例的新思想和扩展持开放态度。
- SQL与数据表示的独立性使其可以用于非关系数据:CSV, JSON和所有大数据格式。有些人把关系模型表示的刚性和SQL的刚性混为一谈。实际上,对于任何给定的Schema,SQL允许你对任何数据格式执行select-join-group-aggregate-project操作
从目前的趋势,由DSL包装SQL会成为越来越好玩的一个趋势。
DSL
下面分析几个最近开源的DSL工具:
Edgedb
这是第一个开源的图关系型数据库。它提供了什么很强的功能呢?
- 丰富的类型系统
- 声明性编程模式,让你表达继承、计算属性、函数、复杂的约束(以及在不久的将来,访问控制规则)
- 一个内置的迁移系统,可以自动或交互地推理和改变模式
- PostgreSQL支持
那什么是图关系型数据库?
EdgeDB是建立在关系数据模型的扩展上,我们称之为 图-关系模型。这个模型完全消除了对象-关系的阻抗不匹配,同时保留了经典关系模型的坚实基础和性能。这使得EdgeDB成为应用开发的理想数据库。
为什么我们需要一种新的数据库?
开发者使用现有数据库的经验其实已经不太好了。我们不应该继续用上个世纪的数据库API架构来浪费我们的生产力。在过去的20年里,数据库后端实现有了惊人的进步,但它还是在大型机时代设计的界面后面徘徊。我们想解决这个问题,并将数据库API和开发人员的体验带入现代应用开发的时代。
它是如何工作的?
EdgeDB重新实现了整个数据库前端:协议、查询语言、模式定义、客户端库和工具。后台是PostgreSQL,但EdgeDB将为你运行它,而你不必知道它的存在。
会比SQL做的更好!
关于SQL的不足之处,其他人都已经写了很多文字。我们可以而且必须努力做得更好,以推动行业的发展。EdgeQL就是这样一种尝试。我们并不是在假装这是一件容易的事情。这是一项巨大的工程。在某种意义上,从头开始建立一个新的SQL数据库比用新的数据模型和新的查询语言重铸PostgreSQL更安全。然而,我们已经开始看到这个赌注得到了回报,EdgeQL的早期用户提供了压倒性的积极反馈,而我们将EdgeQL原生嵌入TypeScript的经验也好于预期。
如果你还不熟悉EdgeQL,下面是一些使用性总结:
- 建立在图-关系模型上:数据关系是一个一流的概念,没有必要进行冗长的连接
- 本质上是可组合的:所有东西都是一个表达式,只有一类值
- 支持直截了当的导航和操作非复杂的数据图
一图胜千言,让我们看看一个查询的例子。
Task:获得一组Zendaya扮演角色的电影,并为每部这样的电影计算平均评论分数,同时检索出按片酬顺序排列的前5名演员名单。下面是用EdgeQL编写这种查询的方法:
select
Movie {
title,
rating := math::mean(.ratings.score)
actors: {
name
} order by @credits_order
limit 5,
}
filter
"Zendaya" in .actors.name
下面是标准SQL的转换:
SELECT
title,
Actors.name AS actor_name,
(SELECT avg(score)
FROM Movie_Reviews
WHERE movie_id = Movie.id) AS rating
FROM
Movie
CROSS JOIN LATERAL (
SELECT name
FROM
Movie_Actors
INNER JOIN Person
ON Movie_Actors.person_id = Person.id
WHERE Movie_Actors.movie_id = Movie.id
ORDER BY Movie_Actors.credits_order
FETCH FIRST 5 ROWS ONLY
) AS Actors
WHERE
'Zendaya' IN (
SELECT Person.name
FROM
Movie_Actors
INNER JOIN Person
ON Movie_Actors.person_id = Person.id
)
这两个查询并不完全等同,因为SQL查询实际上并没有把 Actors
作为一个嵌套的集合来选择,但我们愿意在这里忽略这一点。查询文本大小的差异是惊人的,而这仅仅是一个层次的嵌套。
这不仅仅是个数据库服务器
EdgeDB 的使命是为开发者提供令人敬畏的数据库超能力,但这并不局限于数据库服务器。我们明白,即使是最好的数据库实现,也会被一个糟糕的数据库客户端无望地阻碍。这就是为什么我们为常见的编程语言编写(并将继续编写)全功能的第一方数据库客户端实现(目前可用于Python、JavaScript/TypeScript/Deno和Go)。唉,不可能一下子为每一种语言都写一个客户端,所以我们为客户端实现者提供详尽的文档,以及通用的一致性测试套件。尽管比起SQL来,把EdgeQL查询写成代码中的字符串要愉快得多,但这并不是一种最佳的体验。幸运的是,EdgeQL被设计成一个容易编译的目标,因此我们的目标是提供不妥协的方式,使用你的编程语言的语法和习性来表达EdgeQL查询。
下面是之前用纯TypeScript表达的电影查询:
e.select(
e.Movie, (movie) => ({
title: true,
rating: e.math.mean(movie.reviews.score),
actors: (actor) => ({
name: true,
order_by: actor["@credits_order"],
limit: 5,
}),
filter: e.op("Zendaya", "in", movie.actors.name)
})
)
值得注意的是,它遵循原始的EdgeQL查询结构,并且几乎是相同的大小。作为回报,你可以通过内省模式自动生成的类型定义得到一个完全类型检查的查询后端。我们对这一结果非常满意,它证明了EdgeQL可以很好地集成到现代语言中,进一步减少认知开销。最后但并非最不重要的是我们全面的CLI,它远远超出了REPL和传统的数据库客户端命令,包括轻松安装和管理本地数据库实例(以及即将到来的管理云实例)的命令,互动地创建和应用数据库迁移,以及更多。
Malloy
Malloy是一种用于描述数据关系和转换的实验性语言。它既是一种语义建模语言,也是一种查询语言,可以针对关系型数据库运行查询。Malloy目前可用于BigQuery和Postgres。
它提供了以下特性:
- 查询可编译为SQL,并针对您的数据库进行优化
- 计算是模块化的,可组合的,可重用的,可扩展的,符合现代编程范式的。
- 擅长查询和生成嵌套数据集
- 解决了扇形陷阱,使其有可能在一次查询中聚合任何东西,并减少对事实表和过于复杂的SQL的需要
- 默认值是智能的,语言是简洁的(而SQL是冗长的,往往是多余的)。
Malloy是一种适用于任何正在使用SQL的开发者的语言。无论你是分析师、数据科学家、数据工程师,还是建立数据应用的人。如果你知道SQL,Malloy会感觉很熟悉,同时更加强大和高效。
Malloy允许你边在编程边建模,所以在你开始回答复杂的问题之前,你不需要繁重的前期工作,而且你永远不会被模型牵制或限制。
Malloy已经建立了一个Visual Studio Code扩展,以方便使用Malloy与你的数据进行交互。该扩展提供了一个丰富的环境来创建Malloy数据模型,查询和转换数据,并创建简单的可视化和仪表盘。
给个最简单的一目了然的helloworld:
query: table('malloy-data.faa.flights') -> {
where: origin: 'SFO'
group_by: carrier
aggregate: [
flight_count is count()
average_flight_time is flight_time.avg()
]
}
转换为 SQL 之后会变成:
SELECT
carrier,
COUNT(*) as flight_count,
AVG(flight_time) as average_flight_time
FROM `malloy-data.faa.flights`
WHERE origin = 'SFO'
GROUP BY carrier
ORDER BY flight_count desc -- malloy automatically orders by the first aggregate
总结
数据库后端实现有了惊人的进步,没有理由还在固步自封。DSL虽然不是银弹,但是好玩而且更加方便的DSL去封装复杂的SQL,以及未来甚至带来更好的SQL优化。
想想都很酷啊!!!!