SQL 特性与怪癖
和所有编程语言与库一样,Goose 也有自己的一些特性与不一致之处。 其中一部分是这位“羽毛朋友”演进过程中的历史遗留;另一部分则不可避免,因为我们努力遵循 SQL Standard,并尽量贴近 PostgreSQL 方言(例外情况见 “PostgreSQL Compatibility” 页面)。 其余情况可能只是偏好不同,或者我们其实也认同 应该 怎么做,只是暂时还没来得及实现。
目前我们能做的最好方式就是明确说明这些特性,因此在下方整理了一些示例。
空分组聚合
在空分组上,聚合函数 sum、list 和 string_agg 分别返回 NULL,而不是 0、[] 和 ''。这是 SQL Standard 的规定,我们所知的 SQL 实现都遵循这一行为。列表聚合 list_sum 继承了这一行为,但 Goose 原生的 list_dot_product 不遵循该规则,它在空列表上会返回 0。
从 0 开始还是从 1 开始的索引
为符合标准 SQL,几乎所有地方都使用从 1 开始的索引,例如数组与字符串的索引和切片,以及窗口函数(row_number、rank、dense_rank)。但与 PostgreSQL 类似,JSON 功能使用从 0 开始的索引。
类型
UINT8 与 INT8
UINT8 和 INT8 是不同宽度整数类型的别名:
UINT8对应UTINYINT,因为它是 8-bit 无符号整数INT8对应BIGINT,因为它是 8-byte 有符号整数
解释:数值类型 INTn 和 UINTn 中的 n,表示该数值宽度可以按字节或按位解释。
INT1、INT2、INT4 对应字节数,而 INT16、INT32 和 INT64 对应位数。
UINT 系列同理。
不过,n = 8 既可以表示 8 位,也可以表示 8 字节。
对于无符号类型,UINT8 对应 UTINYINT(8 bits)。
对于有符号类型,INT8 对应 BIGINT(8 bytes)。
表达式
可能让你意外的结果
| Expression | Result | Note |
|---|---|---|
-2^2 | 4.0 | 为兼容 PostgreSQL,一元负号的优先级高于幂运算符。为避免误解,请加括号(例如 -(2^2))或使用 pow function(例如 -pow(2, 2))。 |
't' = true | true | 与 PostgreSQL 兼容。 |
1 = '1' | true | 与 PostgreSQL 兼容。 |
1 = ' 1' | true | 与 PostgreSQL 兼容。 |
1 = '01' | true | 与 PostgreSQL 兼容。 |
1 = ' 01 ' | true | 与 PostgreSQL 兼容。 |
1 = true | true | 与 PostgreSQL 不兼容。 |
1 = '1.1' | true | 与 PostgreSQL 不兼容。 |
1 IN (0, NULL) | NULL | 如果你将输入和输出中的 NULL 理解为 UNKNOWN,这个结果就很合理。 |
1 in [0, NULL] | false | |
concat('abc', NULL) | abc | 与 PostgreSQL 兼容。list_concat 的行为类似。 |
| `'abc' | NULL` |
NaN 值
'NaN'::FLOAT = 'NaN'::FLOAT 和 'NaN'::FLOAT > 3 违反了 IEEE-754,但这意味着浮点类型像其他数据类型一样具有全序关系(请注意其对 greatest / least 的影响)。
age 函数
age(x) 的定义是 current_date - x,而不是 current_timestamp - x。这是另一个继承自 PostgreSQL 的特性。
Extract 函数
list_extract / map_extract 在键不存在时返回 NULL。struct_extract 则会报错,因为结构体的键更像是列名。
子句
SELECT 中的自动列去重
列名会自动去重,且第一次出现的列会遮蔽后续同名列:
CREATE TABLE tbl AS SELECT 1 AS a;
SELECT a FROM (SELECT *, 2 AS a FROM tbl);
| a |
|---|
| 1 |
SELECT 列时的大小写不敏感
由于大小写不敏感,如果 file.parquet 中名为 A 的列出现在目标列 a 之前,就无法通过 SELECT a FROM 'file.parquet' 选中该目标列。
USING SAMPLE
USING SAMPLE 子句在语法位置上位于 WHERE 和 GROUP BY 之后(与 LIMIT 相同),但在语义执行顺序上早于这两者(与 LIMIT 不同)。