SQL 中的前缀别名
简而言之:现在您可以在 DuckDB 的 SQL 方言中使用冒号将别名放在前面,例如:SELECT a: 42;
语法
“也许我们应该让大自然顺其自然,保留它那简单的、一根筋的模式。”
— Dr. Alphonse Mephesto,南方公园第五集
在我们心爱的 SQL 中,通常有不止一种方法可以做某事。 例如,您可以在 WHERE
子句中隐式(且危险地)定义连接条件,或者使用(更好的)JOIN ... ON ...
语法。 一般来说,拥有“不止一种做事方式”可能会让人困惑,甚至可能完全 有时很危险。
话虽如此,我们在 DuckDB 以 更友好的 SQL 为荣。有太多人手工输入这些东西,尤其是在更加临时的分析世界中。
如果有一个好的理由来扩展 SQL 语法,使其更有用,我们至少会考虑它。 其他人似乎正在密切关注。 例如,我们的 GROUP BY ALL
语法现在已被 几乎 所有 SQL 系统 (以及它们的兄弟)采用。
别名
在 SQL 中,用户可以为许多事物定义别名,例如 SELECT
表达式、表名、子查询等。这有时只是为了在结果中具有可读的列名,有时需要在 ORDER BY
子句中引用复杂的表达式,而无需重复它并祈祷优化器。 别名在使用 AS
后定义,但实际上输入 AS
术语是可选的。 例如,这两个语句是等价的
SELECT 42 AS fortytwo;
SELECT 42 fortytwo;
我们可以看到别名跟随表达式 (42
),并且 AS
是可选的。让别名放在它所描述的事物之后在编程中实际上有些罕见,更典型的是首先定义别名,然后提供表达式。 例如,在 C 语言中
int fortytwo = 42;
似乎首先声明别名是一个好主意,毕竟,这是我们稍后要引用这个东西的方式。 像 SQL 那样强制采用相反的方式只会增加心理负担。 此外,如果查询中有几个复杂的表达式,将别名放在最后会使它们很难找到。 例如,这里是臭名昭著的 TPC-H 查询 1 的前几行
SELECT
l_returnflag,
l_linestatus,
sum(l_quantity) sum_qty,
sum(l_extendedprice) sum_base_price,
sum(l_extendedprice * (1 - l_discount)) sum_disc_price,
sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) sum_charge,
avg(l_quantity) avg_qty,
avg(l_extendedprice) avg_price,
avg(l_discount) avg_disc,
count(*) count_order
...
很难在这里找到所有的别名,这甚至还不是一个复杂的例子。 如果你可以将别名放在 SQL 的前面呢? 好吧,别再等待了。
DuckDB 中的前缀别名
在最新的 DuckDB 版本 1.2.0 中,我们悄悄地发布了另一个有用的(我们认为)语法扩展,允许使用冒号 (:
) 语法将别名放在它命名的事物之前。 事实证明,这比我们想象的要容易得多,“所有”需要做的就是修改我们从 Postgres 继承的 Bison 解析器(但我们正在替换它)。 这是之前的例子
SELECT fortytwo: 42;
前缀别名也适用于表名,例如
SELECT *
FROM my_table: some_other_table;
如果需要,可以使用双引号引用别名
SELECT "forty two": 42;
前缀别名几乎可以用于命名所有事物,例如 SELECT
子句中的表达式、函数调用和子查询
SELECT
e: 1 + 2,
f: len('asdf'),
s: (SELECT 42);
它们也可以应用于 FROM clause
中的函数调用和子查询,例如
SELECT *
FROM
r: range(10),
v: (VALUES (42)),
s: (FROM range(10))
请注意,VALUES
子句和带有 FROM
的子查询需要额外的括号才能在此处工作,这是为了安抚邪恶的 Bison 解析器生成器所必需的。 让我们看看之前的 Q1 示例,但这次使用前缀别名
SELECT
l_returnflag,
l_linestatus,
sum_qty: sum(l_quantity),
sum_base_price: sum(l_extendedprice),
sum_disc_price: sum(l_extendedprice * (1-l_discount)),
sum_charge: sum(l_extendedprice * (1-l_discount) * (1+l_tax)),
avg_qty: avg(l_quantity),
avg_price: avg(l_extendedprice),
avg_disc: avg(l_discount),
count_order: count(*)
...
使用 :
或 AS
之间没有语义差异,它们会产生相同的查询结构。
鸣谢
这个想法归功于 Looker 的资深人士 Michael Toy。 我们感谢他的建议。 我们还要感谢传奇人物 Lloyd Tabb 向我们推荐 Michael 的想法。 还可以看看 Mark Needham 的 关于 DuckDB 前缀别名的视频。