概述¶
SQL虽是一门古老的技术,但却并非停滞不前。看下历代SQL标准年表:
SQL-86 SQL-89 SQL-92 SQL:1999 SQL:2003 SQL:2006 SQL:2008 SQL:2011 SQL:2016 SQL:2023
标准这种东西迭代太快也不行,不然就算程序员们的头发能受了,数据库厂家也受不了,所以目前SQL标准这种稳健的迭代是比较正确的做法。
但即使这样,数据库厂家们也没能跟上节奏,至今没有任何一家数据库产品声称完全符合SQL:2016
的核心特性。
SQL
标准每隔几年就会发布一次,国际标准化组织(ISO)于2023年6月1日正式发布了最新SQL
标准,也就是SQL:2023
。Peter Eisentraut在这篇文章有详细的示范说明:
https://peter.eisentraut.org/blog/2023/04/04/sql-2023-is-finished-here-is-whats-new
文章对SQL:2023
标准进行了分析,并归类了三大类别方面的变化:
- 当前SQL语言的一些小变更
- JSON相关的功能特性
- 新引入图查询的接口
文章围绕这三方面进行了示例说明,同时作者又紧接着分析了PostgreSQL数据库对SQL:2023标准的支持情况,参考文章如下:
https://peter.eisentraut.org/blog/2023/04/18/postgresql-and-sql-2023
PostgreSQL supports most of the major features of SQL:2023. Out of 177 mandatory features required for full Core conformance, PostgreSQL conforms to at least 170. In addition, there is a long list of supported optional features. It might be worth noting that at the time of writing, no current version of any database management system claims full conformance to Core SQL:2023.
UNIQUE null treatment (F292) | PostgreSQL 15 |
---|---|
ORDER BY in grouped table (F868) | ancient |
GREATEST and LEAST (T054) | ancient |
String padding functions (T055) | ancient |
Multi-character TRIM functions (T056) | ancient |
Optional string types maximum length (T081) | ancient |
Enhanced cycle mark values (T133) | PostgreSQL 14 |
ANY_VALUE (T626) | PostgreSQL 16 |
Non-decimal integer literals (T661) | PostgreSQL 16 |
Underscores in numeric literals (T662) | PostgreSQL 16 |
JSON data type (T801) | PostgreSQL 9.2/9.4 |
Enhanced JSON data type (T802) | future |
String-based JSON (T803) | not planned |
Hex integer literals in SQL/JSON path language (T840) | PostgreSQL 16 |
SQL/JSON simplified accessor (T860–T864) | future |
SQL/JSON item methods (T865–T878) | future |
JSON comparison (T879–T882) | PostgreSQL 9.4 |
Property Graph Queries | future |
Notes:
- "ancient" means sometime before PostgreSQL 10. These are features that were already widely available in SQL implementations before they were added to the standard.
- As of this writing, PostgreSQL 16 is in feature freeze but not released yet. So those entries could in theory change, but it’s pretty unlikely. Conformance information for PostgreSQL 16 relative to SQL:2023 can also be found in the documentation.
- For the T801 entry, the
json
type was added in PostgreSQL 9.2 and thejsonb
type was added in PostgreSQL 9.4. Most of the JSON functionality in SQL:2016 and SQL:2023 (including features T879 ff.) maps more readily to thejsonb
type, so this information should be interpreted that way. Whether and how thejson
andjsonb
types should be consolidated in PostgreSQL is an open question. - “not planned” is my opinion that the feature is essentially obsolete (see previous article) and not worth implementing.
- “future” is my opinion that the feature could be a sensible addition to PostgreSQL, but there is no concrete work in progress.
SQL 增强¶
UNIQUE 约束中的空值(F292)¶
这个功能用于定义唯一约束中是否允许多个空值。例如:
CREATE TABLE t1 (
a int,
b int,
c int,
UNIQUE (a, b, c)
);
INSERT INTO t1 VALUES (1, NULL, NULL);
INSERT INTO t1 VALUES (1, NULL, NULL); -- ?
问题在于第二个插入语句是否会违法唯一约束。
显然,已有的标准没有明确的定义。为了解决这个问题,新标准增加了一个选项。例如:
CREATE TABLE t2 (
a int,
b int,
c int,
UNIQUE NULLS DISTINCT (a, b, c) -- NULLS DISTINCT 选项将多个空值看作不同的数据,因此不会违反唯一约束
);
INSERT INTO t2 VALUES (1, NULL, NULL);
INSERT INTO t2 VALUES (1, NULL, NULL); -- 正确
CREATE TABLE t3 (
a int,
b int,
c int,
UNIQUE NULLS NOT DISTINCT (a, b, c) -- NULLS NOT DISTINCT 选项将多个空值看作相同的数据,因此违反了唯一约束。
);
INSERT INTO t3 VALUES (1, NULL, NULL);
INSERT INTO t3 VALUES (1, NULL, NULL); -- 错误
该选项的默认值取决于数据库产品的具体实现,这样可以兼容已有的行为。
分组表中的 ORDER BY(F868)¶
我们来看一个示例:
CREATE TABLE product (
product_id int PRIMARY KEY,
product_name varchar,
product_code varchar UNIQUE
);
CREATE TABLE product_part (
product_id int,
part_id int,
num int,
PRIMARY KEY (product_id, part_id)
);
SELECT product.product_id, sum(product_part.num)
FROM product JOIN product_part ON product.product_id = product_part.product_id
GROUP BY product.product_id
ORDER BY product.product_code;
以上示例在大多数 SQL 实现中都能正常工作,但是标准却不允许这种查询方式。也就是说,SQL标准
不允许使用没有出现在SELECT
列表中的分组表字段(product.product_code)进行排序。新标准则允SQL
实现明确支持这种行为。
作为用户,我们并不需要关心这个问题。
GREATEST 和 LEAST 函数(T054)¶
这个功能增加了两个新的标量函数:GREATEST
(最大值)以及 LEAST
(最小值)。它们不是聚合函数 MAX
和MIN
,而是返回一组参数中的最大值和最小值。实际上很多数据库已经实现了这两个函数:
SELECT greatest(1, 2); --> 2
SELECT least(1, 2); --> 1
SELECT greatest(1, 2, 3); --> 3
SELECT least(1, 2, 3); --> 1
字符串填充函数(T055)¶
这个功能增加了两个新的字符串函数:LPAD
(左侧填充)以及 RPAD
(右侧填充)。这两个函数同样在很多数据库中已经有了实现:
SELECT lpad(cast(12345.67 as varchar), 12, '*');
-- ****12345.67
默认使用的填充字符为空格。
多字符 TRIM 函数(T056)¶
已有的单字符截断函数(TRIM({LEADING|TRAILING|BOTH} 'x' FROM val))
只能指定要截断的单个字符,新函数则可以指定多个需要截断的字符,而且语法更加简洁。例如:
SELECT ltrim('****12345.67', '*');
可选的字符串类型最大长度(T081)¶
这个功能允许不指定 VARCHAR
类型的最大长度。在此之前,我们经常会看到为了支持任意长度而指定一个很大的长度值:
CREATE TABLE t1 (
a VARCHAR(1000),
b VARCHAR(4000),
-- ...
);
现在可以简单的指定数据类型:
CREATE TABLE t1 (
a VARCHAR,
b VARCHAR,
-- ...
);
这种情况下,字段的最大长度取决于数据库实现。
增强的递归循环标识(T133)¶
CYCLE
子句是鲜为人知的一个递归查询功能,可以用于检测循环递归:
WITH RECURSIVE ... (
SELECT ...
UNION ALL
SELECT ...
)
CYCLE id SET is_cycle TO 'Y' DEFAULT 'N' USING path;
这种方式可以基于 id 字段检测是否出现循环递归,并且将 is_cycle 字段设置为指定值。
当 SQL
标准增加递归循环时,还没有布尔类型,因此使用了一个字符串。如今 SQL
标准提供了布尔类型,因此上面的查询可以改进为:
WITH RECURSIVE ... (
SELECT ...
UNION ALL
SELECT ...
)
CYCLE id SET is_cycle USING path;
ANY_VALUE 函数(T626)¶
这个新的聚合函数可以从一组数据中返回任意一个非空值。例如:
CREATE TABLE t1 (
a int,
b int
);
INSERT INTO t1 VALUES (1, 11), (1, 22), (1, 33);
SELECT a, any_value(b)
FROM t1
GROUP BY a;
以上查询可能会返回(1 | 11)、(1 | 22)或者(1 | 33)。
非十进制的整数常量(T661)¶
这个新功能支持十六进制、八进制以及二进制的整数常量。
SELECT 0xFFFF, 0o755, 0b11001111;
数字常量中的下划线(T662)¶
这个功能允许在数字常量中使用下划线,提高可读性。
SELECT ... WHERE a > 1_000_000;
UPDATE ... SET x = 0x_FFFF_FFFF ...
JSON 新功能¶
JSON 数据类型(T801)¶
新标准增加了 JSON
数据类型。许多数据库产品实际上已经提供了这个类型。
SQL:2016
只是支持了 JSON
数据的操作,数据仍然使用字符串存储。新的功能同时还增加了一些 JSON
相关的函数,例如 JSON_SERIALIZE
、JSON_SCALAR
以及 IS JSON
。
增强的 JSON 数据类型(T802)¶
这个功能可以为 JSON
类型指定额外的选项。具体来说,可以指定一个唯一键检测,例如 JSON('...text...' WITH UNIQUE KEYS)
。
基于字符串的 JSON(T803)¶
这个功能代表了 SQL:2016
中的字符串 JSON
。数据库提供的 JSON
函数(例如 JSON_OBJECT
、JSON_OBJECTAGG
、JSON_TABLE
等)可以选择支持字符串格式的 JSON
数据还是原生的 JSON
数据类型,也可以支持两者。
SQL/JSON 路径中的十六进制整数常量(T840)¶
这个功能允许在 SQL/JSON
路径语言中使用十六进制整数常量。
虽然不属于这个功能,但是 SQL
标准现在允许以扩展插件的形式支持 JavaScript(ECMAScript)
,因此不需要每次 ECMAScript
更新时更新 SQL
标准。所以 SQL/JSON
路径现在也可以支持最新 ECMAScript
中的其他数字常量,例如二进制整数和下划线分隔符。
SQL/JSON 简化访问符(T860-T864)¶
"简化访问符"功能允许使用点号和数组语法访问 JSON
数据中的复合类型和数组类型。例如,假设字段 j 是一个 JSON
类型,包含以下数据:
{"foo": {"bar": [100, 200, 300]}, ...}
我们可以使用简化访问符获取字段中的信息:
SELECT t.j.foo.bar[2], ...
FROM tbl t ...
SQL/JSON 元素方法(T865-T878)¶
SQL/JSON
元素方法或者函数可以在 SQL/JSON
路径表达式中使用,对元素值进行处理。SQL:2016
已经定义了一些这类方法,例如 abs()、floor()、size()。新增的方法主要用于数据类型转换:
- T865: SQL/JSON item method: bigint()
- T866: SQL/JSON item method: boolean()
- T867: SQL/JSON item method: date()
- T868: SQL/JSON item method: decimal()
- T869: SQL/JSON item method: decimal() with precision and scale
- T870: SQL/JSON item method: integer()
- T871: SQL/JSON item method: number()
- T872: SQL/JSON item method: string()
- T873: SQL/JSON item method: time()
- T874: SQL/JSON item method: time_tz()
- T875: SQL/JSON item method: time precision
- T876: SQL/JSON item method: timestamp()
- T877: SQL/JSON item method: timestamp_tz()
- T878: SQL/JSON item method: timestamp precision
JSON 数据比较功能(T879-T882)¶
这个功能允许针对 JSON
数据类型的比较、排序以及分组操作。
属性图查询语言¶
这是 SQL
标准中增加的一个全新部分:Property Graph Queries (SQL/PGQ)
。这个新功能支持使用图数据库的方式查询表中的数据。
完整的属性图查询语言比较复杂,以下是一个简单的示例:
CREATE TABLE person (...);
CREATE TABLE company (...);
CREATE TABLE ownerof (...);
CREATE TABLE transaction (...);
CREATE TABLE account (...);
CREATE PROPERTY GRAPH financial_transactions
VERTEX TABLES (person, company, account)
EDGE TABLES (ownerof, transaction);
SELECT owner_name,
SUM(amount) AS total_transacted
FROM financial_transactions GRAPH_TABLE (
MATCH (p:person WHERE p.name = 'Alice')
-[:ownerof]-> (:account)
-[t:transaction]- (:account)
<-[:ownerof]- (owner:person|company)
COLUMNS (owner.name AS owner_name, t.amount AS amount)
) AS ft
GROUP BY owner_name;
以上示例中,表之间还需要定义外键,这样属性图定义才能确定它们之间的联系;或者也可以在属性图定义时指定表之间的联系。