#15 Dune SQL 查询引擎入门
Dune 已经正式推出了其团队基于Trino(https://trino.io/)自研的查询引擎Dune SQL。本文介绍Dune SQL的一些常见查询语法、注意事项和细节。
注:由于Dune已经宣布2023年下半年起将全面过渡到Dune SQL查询引擎,所以本篇教程将原有的所有Query全部升级到了Dune SQL 版本。
Dune SQL 语法概览
Dune SQL需要注意的书写语法要点有几个:
Dune SQL 使用双引号来引用包含特殊字符或者本身是关键字的字段名或表名,如
"from", "to"。Dune SQL的字符串类型和常用数值类型分别是
varchar、double和decimal(38, 0)。Dune SQL 不支持隐式类型转换。比如,Dune SQL中,不能将
'2022-10-01'直接与 block_time 进行比较,需要用date('2022-10-01')等函数显式转换为日期后才能比较。不能直接将数值类型和字符串连接,要用cast(number_value as varchar)转换为字符串后才能连接。
Dune 文档提供了一份比较详细的语法对照表,链接是:Syntax Comparison,大家可以参考。下图列出了部分差异对照:

Dune SQL 实例
Dune SQL使用双引号引用特殊字段名和表名
Dune SQL使用双引号
日期时间
Dune SQL 不支持字符串格式的日期值隐式转换为日期时间类型的值,必须使用显式转换。可以使用日期时间函数或者日期时间操作符。
使用日期值
Dune SQL使用date()函数
使用日期时间值
Dune SQL使用timestamp 操作符
使用interval
Dune SQL使用interval '12' hour
地址和交易哈希
Dune SQL 查询中,地址和哈希值可以不放入单引号中直接使用,此时大小写不敏感,可以不显示转换为小写格式。
Dune SQL的字符串类型 varchar 和数值类型 double
Dune SQL中的字符串和常用数值类型是varchar和double。Dune SQL中的整数值默认是bigint类型,在做一些大数字的乘法时,容易产生溢出错误,此时可以强制转换为double类型或者decimal(38, 0)类型。Dune SQL中进行整数除法也不会隐式转换为浮点数再进行相除,而是直接返回一个整数,这点也需要注意。
转换为字符串
Dune SQL
检查上面的SQL输出,可以看到当将比较大或者比较小的数字直接cast()转换为字符串时,会被处理为科学计数法的输出格式,效果不太理想。使用format()则可以精确控制输出的字符串的格式,所以推荐用这种方式。
转换为数值
注意,表erc20_ethereum.evt_Transfer中,value字段的类型是字符串。可以使用cast()函数将其转换为double 或者 decimal(38, 0) 数值类型。
强制类型转换
如前所述,Dune SQL不支持隐式类型转换,当我们需要将两种不同类型的值进行比较或者执行某些操作的时候,就需要确保它们是相同的(兼容的)数据类型,如果不是,则需要使用相关的函数或者操作符进行显式的类型转换。否则可能会遇到类型不匹配相关的错误。这里再举一个简单例子:
Dune SQL未做类型转换时,下面的SQL会报错:
Dune SQL显式类型转换,可以执行
当我们遇到类似"Error: Line 47:1: column 1 in UNION query has incompatible types: integer, varchar(1) at line 47, position 1."这种错误时,就需要处理相应字段的类型兼容问题。
转换为double类型解决数值范围溢出错误
Dune SQL 支持整数类型 int 和 bigint,但是由于EVM等区块链不支持小数导致数值经常很大,比如当我们计算gas 费的时候,就可能遇到数值溢出的错误。下面的SQL,为了故意导致错误,我们将计算的gas fee乘以1000倍了:
执行上面的SQL将会遇到错误:
为了避免类型溢出错误,我们可以将第一个参数显式转换为double类型。下面的SQL可以正确执行:
转换为double类型解决整数相除不能返回小数位的问题
同样,如果两个数值是bigint 类型,二者相除默认返回的也是整数类型,小数部分会被舍弃。如果希望返回小数部分,可以将被除数显式转换为double类型。
执行上面的SQL,gas_used_percentage的值将会是0或者1,小数部分被舍弃取整,显然这不是我们想要的结果。将被除数gas_used显式转换为double类型,可以得到正确结果:
从Hex十六进制转换到十进制
Dune SQL 定义了一组新的函数来处理将varbinary类型字符串转换到十进制数值的转换,字符串必须以0x前缀开始。
详细帮助可以参考:Byte Array to Numeric Functions
生成数值序列和日期序列
数值序列
Dune SQL生成数值序列的语法:
日期序列
Duen SQL使用unnest()搭配sequence()来生成日期序列值并转换为多行记录。
Dune SQL生成日期序列的语法:
数组查询
Dune SQL 使用
cardinality()查询数组大小。
Dune SQL语法:
Dune SQL 数组的索引从 1 开始计数
Dune SQL访问数组元素:
将数组元素拆分到多行记录。
Dune SQL拆分数组元素到多行:
同时将多个数组字段拆分到多行记录。
要同时将多个数组字段拆分到多行(前提是它们必须具有相同的长度),Dune SQL中可以在unnest()函数中包括多个字段,同时输出多个对应字段。
Dune SQL拆分多个数组元素到多行:
从Spark SQL迁移查询到Dune SQL 示例
将已经存在的Spark SQL引擎编写的query迁移到Dune SQL的过程是非常便利的。你可以直接进入Query的Edit界面,从左边的数据集下拉列表中切换到“1. v2 Dune SQL”,同时对Query的内容做相应的调整,涉及的主要修改已经在本文前面各节分别进行了介绍。这里举一个实际的例子:
Spark SQL 版本:https://dune.com/queries/1773896 Dune SQL 版本:https://dune.com/queries/1000162
迁移时修改内容对照:

其他
Dune SQL 还有一个潜在的高级功能,就是允许针对一个已保存的查询进行查询(Query of Query)。这个功能有很多的想象空间,可简化查询逻辑,优化缓存使用等。比如,你可以将一个复杂的查询的基础部分保存为一个query,然后基于此query来进一步的汇总统计。这个功能貌似有时还不太稳定。不过大家可以试试。
参考链接
SixdegreeLab介绍
SixdegreeLab(@SixdegreeLab)是专业的链上数据团队,我们的使命是为用户提供准确的链上数据图表、分析以及洞见,并致力于普及链上数据分析。通过建立社区、编写教程等方式,培养链上数据分析师,输出有价值的分析内容,推动社区构建区块链的数据层,为未来广阔的区块链数据应用培养人才。
欢迎访问SixdegreeLab的Dune主页。
因水平所限,不足之处在所难免。如有发现任何错误,敬请指正。
Last updated
Was this helpful?