SQL 标准的版本与 SQL 方言
第 1.8 课 · 阅读时间:约 8 分钟
SQL 标准是 SQL 语言的国际规范,由 ISO 和 ANSI 以 ISO/IEC 9075 的编号发布。第一版发布于 1986 年,最新版本是 SQL:2023。本课介绍标准的每个版本新增了什么,从显式的 JOIN 语法到 JSON 和图查询,以及为什么每个数据库都有自己的 SQL 方言。学完本课,你会知道查询中哪些部分可以通用,哪些取决于具体的数据库。
在上一课中,你了解了什么是 SQL 以及基本查询的结构。SQL 不属于某一家厂商:它的核心由标准定义,而每个数据库都以自己的方式实现这个标准。
这也解释了一个常见的意外:在 PostgreSQL 中能运行的查询,在 SQL Server 或 MySQL 中可能会报错。了解标准,有助于写出在数据库之间迁移时改动更少的查询。
什么是 SQL 标准?
SQL 于 20 世纪 70 年代诞生于 IBM。到 80 年代中期,多家厂商都有了各自版本的语言,于是 ANSI 和随后的 ISO 发布了一份共同的规范:SQL-86。此后,ISO/IEC JTC 1/SC 32 委员会定期修订这一标准。
标准篇幅很大,由多个部分组成。第 2 部分 SQL/Foundation 定义了语言的核心:表、查询、数据修改和事务。其他部分涉及 XML、外部数据、多维数组,以及从 2023 年起的属性图。
有几点值得了解:
- 标准定义了一组必须支持的功能,称为 Core SQL;其余功能都是可选的。
- 没有任何数据库实现了完整的标准,每个数据库也都加入了自己的扩展。
- 标准文档需要付费购买,所以在实践中,人们主要通过数据库文档来学习 SQL。
SQL 标准的各个版本及其新增功能
每个版本都保留了之前的内容,并加入了新功能。下表列出了你在本课程中会遇到的变化:
| 版本 | 主要新增 | 本课程中的位置 |
|---|---|---|
| SQL-86、SQL-89 | SELECT、INSERT、UPDATE、DELETE、CREATE TABLE,主键和外键 | 第 2 模块 |
| SQL-92 | 显式 JOIN ... ON,LEFT、RIGHT 和 FULL OUTER JOIN,CASE、CAST,DATE 和 TIMESTAMP 类型 | JOIN、CASE |
| SQL:1999 | WITH 和递归查询(CTE)、BOOLEAN、触发器、ROLLUP 和 CUBE | CTE、递归 CTE |
| SQL:2003 | 窗口函数、MERGE、标识列、序列、SQL/XML | 窗口函数 |
| SQL:2006 | 使用 XQuery 查询 XML | - |
| SQL:2008 | FETCH FIRST n ROWS ONLY、OFFSET、TRUNCATE TABLE | LIMIT 和 FETCH FIRST |
| SQL:2011 | 时态表(行的历史)、LAG、LEAD、FIRST_VALUE、LAST_VALUE | LAG 和 LEAD |
| SQL:2016 | JSON 函数(JSON_VALUE、JSON_QUERY、JSON_TABLE)、LISTAGG、MATCH_RECOGNIZE | JSON 函数 |
| SQL:2019 | 多维数组(SQL/MDA) | - |
| SQL:2023 | JSON 数据类型、属性图查询(SQL/PGQ)、GREATEST、LEAST、ANY_VALUE | 复杂数据类型 |
SQL-92 至今仍是重要的参照点:它确定了几乎所有关系数据库都支持的语法。但现代标准要丰富得多,分析型 SQL 的大部分功能都是在 1999 年之后出现的。
SQL:2023 的新内容:JSON 和图查询
SQL:2023 让同一个数据库可以在同一条查询中同时处理关系数据、文档数据和图数据。
JSON 数据类型
SQL:2016 加入了读取以文本形式存储的 JSON 的函数。SQL:2023 则加入了真正的 JSON 类型:数据库会检查列中是否是有效的 JSON,并能以高效的二进制形式存储。MySQL、MariaDB 和 PostgreSQL(jsonb)在标准之前就有了各自的 JSON 类型;Oracle 从 21c 版本开始遵循标准类型。
属性图查询(SQL/PGQ)
属性图是一组顶点和边,例如通过“参演”关系连接起来的演员和电影。Neo4j 等图数据库使用专门的语言查询这类数据。标准新增的第 16 部分 SQL/PGQ 允许在普通表之上定义图,并用 SQL 来查询它。
在 Sakila 中,actor 和 film 成为顶点,film_actor 成为连接它们的边:
CREATE PROPERTY GRAPH film_graph
VERTEX TABLES (actor KEY (actor_id), film KEY (film_id))
EDGE TABLES (
film_actor KEY (actor_id, film_id)
SOURCE KEY (actor_id) REFERENCES actor (actor_id)
DESTINATION KEY (film_id) REFERENCES film (film_id)
LABEL acted_in
);
注意:图不会复制数据,它只描述现有的表之间如何关联。
GRAPH_TABLE 运算符按模式查找路径。下面的查询返回与 Penelope Guiness 合演过电影的演员:
SELECT DISTINCT partner
FROM GRAPH_TABLE (film_graph
MATCH (a IS actor) -[IS acted_in]-> (f IS film) <-[IS acted_in]- (b IS actor)
WHERE a.first_name = 'PENELOPE'
AND a.last_name = 'GUINESS'
AND b.actor_id <> a.actor_id
COLUMNS (b.first_name || ' ' || b.last_name AS partner)
);
结果:每位合演演员一行。模式读起来就像一幅图:演员 → 电影 ← 演员。
同样的结果也可以用 film_actor 的两次连接得到,你会在 JOIN 模块中学到这种写法。对于更长的路径,例如“通过两部电影联系起来的演员”,图语法要简短得多。目前 Oracle Database 23ai 支持 SQL/PGQ;PostgreSQL 的支持正在开发中。
SQL 方言:各数据库与标准有何不同
SQL 方言是某个数据库所实现的 SQL 版本:它支持的标准功能加上自己的扩展。基本查询在各处看起来都一样,但细节有所不同。例如,在不同数据库中查询最长的五部电影:
-- 标准(PostgreSQL、Oracle)
SELECT title, length
FROM film
ORDER BY length DESC
FETCH FIRST 5 ROWS ONLY;
-- MySQL、MariaDB、SQLite、PostgreSQL
SELECT title, length
FROM film
ORDER BY length DESC
LIMIT 5;
-- SQL Server
SELECT TOP 5 title, length
FROM film
ORDER BY length DESC;
结果:每种写法都返回相同的五部电影。LIMIT 和 TOP 比标准的 FETCH FIRST 出现得早,至今仍更常见。
其他常见差异:
| 任务 | 标准 | MySQL / MariaDB | PostgreSQL | SQL Server | Oracle | SQLite |
|---|---|---|---|---|---|---|
| 取前 N 行 | FETCH FIRST | LIMIT | LIMIT、FETCH FIRST | TOP、OFFSET ... FETCH | FETCH FIRST | LIMIT |
| 拼接字符串 | a || b | CONCAT(a, b) | a || b | a + b、CONCAT | a || b | a || b |
| 给名称加引号 | "name" | `name` | "name" | [name]、"name" | "name" | "name" |
| 自增主键 | GENERATED AS IDENTITY | AUTO_INCREMENT | GENERATED AS IDENTITY、SERIAL | IDENTITY(1, 1) | GENERATED AS IDENTITY | INTEGER PRIMARY KEY |
| 递归 CTE | WITH RECURSIVE | WITH RECURSIVE | WITH RECURSIVE | WITH | WITH | WITH RECURSIVE |
| 布尔类型 | BOOLEAN | BOOLEAN = TINYINT(1) | BOOLEAN | BIT | BOOLEAN(23ai) | 0 和 1 |
FULL OUTER JOIN | 支持 | 不支持 | 支持 | 支持 | 支持 | 支持(3.39+) |
PostgreSQL 通常被认为最接近标准。MySQL 与标准的差异最多,不过它的新版本也加入了 CTE、窗口函数等标准功能。
如何写出可移植的 SQL
把数据库结构迁移到另一个数据库总要做一些工作,但以下习惯能让查询更贴近标准:
- 数据库支持时,优先使用标准写法:用
CASE而不是IF(),用COALESCE而不是IFNULL或ISNULL,使用显式的JOIN ... ON。 - 使用
CAST(x AS type),而不是 PostgreSQL 的x::type这类简写。 - 避免给名称加引号:小写且不含空格的名称在任何数据库中都不需要引号。
- 把依赖具体数据库的部分,例如行数限制或日期函数,放在明确标注的位置。
在本网站上,同一道题可以在多个数据库上求解,你会在练习中亲眼看到这些差异。下一个模块将从 SELECT 开始编写查询,它是所有数据读取背后的语句。
本课要点:
- SQL 标准是 ISO/IEC 9075;它始于 SQL-86,当前版本是 SQL:2023。
- SQL-92 奠定了共同基础,SQL:1999 和 SQL:2003 加入了 CTE 和窗口函数,SQL:2016 加入了 JSON 函数。
- SQL:2023 加入了
JSON类型和基于普通表的属性图查询(SQL/PGQ)。 - SQL 方言是某个数据库实现的标准,再加上它自己的扩展。
- 基本查询可以通用;差异最大的是行数限制、字符串函数、自增和引号。
常见问题
SQL 标准的最新版本是什么?
最新版本是 SQL:2023(ISO/IEC 9075:2023),于 2023 年 6 月发布。它加入了 JSON 数据类型、属性图查询(SQL/PGQ),以及 GREATEST、LEAST 和 ANY_VALUE 等较小的功能。
ANSI SQL 和 ISO SQL 有什么区别?
如今它们是同一个标准。ANSI 于 1986 年发布了第一版,ISO 于 1987 年采纳了它,此后 ANSI 一直采用 ISO/IEC 9075 标准。“ANSI SQL”只是标准 SQL 的习惯叫法。
有没有数据库完全支持 SQL 标准?
没有。每个数据库都支持 Core SQL 和一组不同的可选功能,并加入自己的扩展。PostgreSQL 通常被认为最接近标准。
现在还值得学习 SQL-92 吗?
SQL-92 的语法是几乎在所有数据库中都能运行的基础,因此是很好的起点。但现代 SQL 还包括 CTE、窗口函数和 JSON,数据分析工作中每天都会用到它们。
面试题
什么是 SQL 方言?
SQL 方言是某个数据库所实现的 SQL 版本:ISO 标准的一部分加上厂商的扩展。例如 SQL Server 的 T-SQL,以及 Oracle、MySQL 和 PostgreSQL 各自的 SQL 方言。基本语法是共通的;行数限制、函数和数据类型等细节各不相同。
说出 MySQL 和 SQL Server 语法的几个区别。
MySQL 用 LIMIT 限制行数,SQL Server 用 TOP 或 OFFSET ... FETCH。MySQL 用 CONCAT 拼接字符串,SQL Server 还可以用 +;MySQL 用反引号给名称加引号,SQL Server 用方括号。自增主键在 MySQL 中写作 AUTO_INCREMENT,在 SQL Server 中写作 IDENTITY。
SQL:2023 给标准加入了什么?
原生的 JSON 数据类型和 SQL/PGQ:用 CREATE PROPERTY GRAPH 和 GRAPH_TABLE 在关系表之上进行属性图查询。它还加入了 GREATEST、LEAST 和 ANY_VALUE 等较小的功能。
如何让 SQL 查询可以在不同数据库之间迁移?
使用标准写法:显式的 JOIN ... ON、CASE、COALESCE、CAST。避免使用某个厂商特有的函数和带引号的标识符,并把必须不同的部分,例如行数限制和日期函数,单独隔离出来。