我最喜欢DuckDB的一点是:创新式SQL语法。最近的一个发现(至少对我来说)是,可以使用点操作符将函数链接起来,就像在许多通用编程语言中一样。在本文中,我们将探索该功能。
从嵌套查询开始
首先从嵌套查询语句开始:
FROM (SELECT unnest(range(1,50,5)) AS num)
SELECTnum,factorial(CAST(log2(power(sqrt(num),3)) AS INTEGER)) AS val;
这个查询是做什么的?对于从1到50的数字,以5为步:
- 计算平方根
- 把该值的三次方
- 计算该值的对数
- 将其转换为整数
- 最后计算阶乘
上面代码相当难以阅读,如果没有在单独行上格式化会更难懂。你必须从中间开始,然后仔细向外扩展。
链式调用
我发现从左到右阅读代码要容易得多,这就是函数链让我们做的。它有效地改写了:
## 一般调用
fn(arg1, arg2, arg3, ...)
## 链式调用
arg1.fn(arg2, arg3, ...)
因此,只有当函数的结果可以作为第一个参数传递给下一个函数时,这种技术才有效。在DuckDB中,大多数函数的签名使这成为可能。
如果我们要重写上面的查询来使用链式调用,我们可以相当容易地做到这一点:
FROM (SELECT range(1,50,5).unnest() AS num)
SELECTnum,CAST(num.sqrt().power(3).log2() AS INTEGER).factorial() AS val;
返回结果:
num|val |
---+------------------+1| 0.0|6| 3.877443751081734|11|5.1891474279559455|16| 6.0|21| 6.588476134168141|26| 7.050659577211638|31| 7.431294465580312|36| 7.754887502163468|41| 8.036328006927125|46| 8.28534293408552|
自定义转换函数
但是现在我们有点卡住了,因为我们不能链接CAST函数。我们可以像普通的那样编写CAST,然后调用factorial:
FROM (SELECT range(1,50,5).unnest() AS num)
SELECTnum,CAST(num.sqrt().power(3).log2() AS INTEGER).factorial() AS val;
虽然解决了cast函数调用问题,但如果我们能把所有东西都锁起来,那还是很酷的。不幸的是,没有可以动态转换值的函数,这有点令人沮丧。但是我们可以做的是自己创建只用于转换为整数的函数:
CREATE OR REPLACE MACRO asInt(value) ASCAST(value AS INTEGER);
现在可以链接所有查询中函数:
FROM (SELECT range(1,50,5).unnest() AS num)
SELECTnum,num.sqrt().power(3).log2().asInt().factorial() AS val;
查询结果:
num|val |
---+-----+1| 1|6| 24|11| 120|16| 720|21| 5040|26| 5040|31| 5040|36|40320|41|40320|46|40320|
总结
是不是很酷,关于如何自定义函数(或宏),下次我们再分享。希望对你有用,一起学习。