此文章是 #Footprint Analytics for SQL 系列 的其中一个章节。
SQL ,又称结构化查询语言 (Structured Query Language),是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。
举个例子:假如你想吃披萨,榴莲味的,你现在又没有空,于是你叫了一个跑腿和跑腿的人说:帮我买一个披萨,要榴莲味的,跑腿就会去披萨店帮你买了一个榴莲味的披萨送到你家里。类别过来SQL就是你对跑腿说的话,Footprint Analytics 就是个跑腿,可以让你和数据湖对话,并将你想要的数据搬出来给你。SQL最基本的语法结构包含三个模块,几乎所有的SQL都包含这三部分:
select: 选择哪个字段?
from: 从哪个表中取数据?
where:筛选或者限制的条件是什么?
下面是这个表里用的比较多的几个字段
block_timestamp 打包交易的时间
hash 这条交易流水的hash
block_number 交易的区块高度
value 转出的ETH(需要除以ETH 代币的18位decimals换算精度,power(10,18))
from_address 转出ETH代币的钱包地址
to_address 转入ETH代币的合约地址
receipt_gas_used 被执行的命令的gas消耗值总和
receipt_effective_gas_price gas该交易中单位gas的价格(用ETH代币计算)
案例1: 查询币圈大佬孙哥的钱包(0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296)在2022年1月份以来的每一笔转出金额大于1000ETH是在什么时候和具体的转出数量已经每笔转出消耗的Gas
SQL
“
select --Select后跟着需要查询的字段,多个字段用英文逗号分隔
block_timestamp
,from_address
,to_address
,hash
,value /power(10,18) as value --通过将value除以/power(10,18)来换算精度,18是ETH代币的精度
,(receipt_gas_used*receipt_effective_gas_price*power(0.1,18)) as gas_fee
from ethereum_transactions --从 ethereum.transactions表中获取数据
where block_timestamp > date '2022-01-01' --限制Transfer时间是在2022年1月1日之后
and from_address = lower('0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296') --限制孙哥的钱包,这里用lower()将字符串里的字母变成小写格式(footprint数据库里存的模式是小写,直接从以太坊浏览器粘贴可能大些混着小写)
and value /power(10,18) >1000 --限制ETH Transfer量大于1000
order by block_timestamp --基于block_timestamp做升序排列,如果想降序排列需要在末尾加desc,因为默认是asc,所以不用末尾不用加asc也行
”
语法说明:
select
from
where
运算符号:and / or
如果筛选条件有多个,可以用运算符来连接
and: 多个条件的并集
or : 多个条件的交集
排序: order by [字段A] , 按照A字段升序,如果需要按照降序排序就在字段末尾加上 desc,因为默认是升序的,所以需要升序的时候不用在末尾加asc
幂乘计算:用户换算精度,函数是Power(Number,Power),其中number表示底数,power表示指数
字符串中字母换算大小写
lower([字段]):字符串中的字母统一换成小写
upper([字段]) :字符串中的字母统一换成小写
SQL:
“
select
sum( value /power(10,18) ) as value --对符合要求的数据的value字段求和
,max( value /power(10,18) ) as max_value --求最大值
,min( value /power(10,18) ) as min_value--求最小值
,count( hash ) as tx_count --对符合要求的数据计数,统计有多少条
,count( distinct to_address ) as tx_to_address_count --对符合要求的数据计数,统计有多少条(按照去向地址to去重)
from ethereum_transactions --从 ethereum.transactions表中获取数据
where block_timestamp > date '2022-01-01' --限制Transfer时间是在2022年1月1日之后
and from_address = lower('0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296') --限制孙哥的钱包,这里用lower()将字符串里的字母变成小写格式(footprint数据库里存的模式是小写,直接从以太坊浏览器粘贴可能大些混着小写)
and value /power(10,18) > 1000 --限制ETH Transfer量大于1000
“
语法说明:
聚合函数
count():计数,统计有多少个;如果需要去重计数,括号内加distinct
sum():求和
min():求最小值
max():求最大值
avg():求平均
3.1 把时间戳转化成小时/天/周的格式,方便进一步做聚合统计
“
select --Select后跟着需要查询的字段,多个字段用英文逗号分隔
block_timestamp -- transactions发生的时间
,date_trunc('hour',block_timestamp) as block_hour -- 转化成小时的粒度
,date_trunc('day',block_timestamp) as block_date -- 转化成天的粒度
,date_trunc('week',block_timestamp) as block_week -- 转化成周的粒度
,from_address
,to_address
,hash
,value /power(10,18) as value --通过将value除以/power(10,18)来换算精度,18是ETH代币的精度
,(receipt_gas_used*receipt_effective_gas_price*power(0.1,18)) as gas_fee
from ethereum_transactions --从 ethereum.transactions表中获取数据
where block_timestamp > date '2022-01-01' --限制Transfer时间是在2022年1月1日之后
and from_address = lower('0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296') --限制孙哥的钱包,这里用lower()将字符串里的字母变成小写格式(footprint数据库里存的模式是小写,直接从以太坊浏览器粘贴可能大些混着小写)
and value /power(10,18) >1000 --限制ETH Transfer量大于100
order by block_timestamp --基于block_timestamp做升序排列,如果想降序排列需要在末尾加desc,因为默认是asc,所以不用末尾不用加asc也行
”
语法说明
date_trunc(‘datepart’,timestamp)
minute:将输入时间戳截断至分钟
hour:将输入时间戳截断至小时
day:将输入时间戳截断至天
week:将输入时间戳截断至某周的星期一
year:将输入时间戳截断至一年的第一天
3.2 基于之前得到的处理后的时间字段,使用 group by + sum 完成分组聚合
”
select --Select后跟着需要查询的字段,多个字段用英文逗号分隔
date_trunc('day',block_timestamp) as block_date -- 转化成天的粒度
,sum(value /power(10,18)) as value --通过将value除以/power(10,18)来换算精度,18是ETH代币的精度
,sum(receipt_gas_used*receipt_effective_gas_price*power(0.1,18)) as gas_fee
from ethereum_transactions --从 ethereum.transactions表中获取数据
where block_timestamp > date '2022-01-01' --限制Transfer时间是在2022年1月1日之后
and from_address = lower('0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296') --限制孙哥的钱包,这里用lower()将字符串里的字母变成小写格式(footprint数据库里存的模式是小写,直接从以太坊浏览器粘贴可能大些混着小写)
and value /power(10,18) >1000 --限制ETH Transfer量大于1000
group by 1 --按照第一个字段做分组,即date_trunc('day',block_timestamp) 转化后的字段
order by block_date --基于block_date,做升序排列,block_date 是用'as'对date_trunc('day',block_time)取别名,也可以写‘1’即表第几个字段,,如果想降序排列需要在末尾加desc,因为默认是asc,所以不用末尾不用加asc也行
“
语法说明:
分组聚合(group by)
SQL
”
select
block_timestamp
,transactions_info.block_hour as block_hour
,from_address
,to_address
,eth_amount
,hash
,price
,eth_amount*price as usd_value – eth 的数量乘以币价的价格
from
(
select --Select后跟着需要查询的字段,多个字段用英文逗号分隔
block_timestamp
,date_trunc('hour',block_timestamp) as block_hour --把block_timestamp用date_trunc处理成小时,方便作为主键去关联
,from_address
,to_address
,hash
,value /power(10,18) as eth_amount --通过将value除以/power(10,18)来换算精度,18是ETH代币的精度
,(receipt_gas_used*receipt_effective_gas_price*power(0.1,18)) as gas_fee
from ethereum_transactions --从 ethereum.transactions表中获取数据
where block_timestamp > date '2022-01-01' --限制Transfer时间是在2022年1月1日之后
and from_address = lower('0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296') --限制孙哥的钱包,这里用lower()将字符串里的字母变成小写格式(footprint数据库里存的模式是小写,直接从以太坊浏览器粘贴可能大些混着小写)
and value /power(10,18) >1000 --限制ETH Transfer量大于1000 order by block_timestamp --基于block_timestamp做升序排列,如果想降序排列需要在末尾加desc,因为默认是asc,所以不用末尾不用加asc也行
) as transactions_info
left join
--将transactions_info与price_info的数据关联,关联方式为 left join
(
select
date_trunc('hour',timestamp) as block_hour --把timestamp用date_trunc处理成小时,方便作为主键去关联
,avg(price) as price
from
"token_price_5min"
where token_symbol = 'ETH' -- 取ETH 代币的数据
and chain ='Ethereum' -- 取以太坊上的价格数据
group by 1
) price_info
on transactions_info.block_hour = price_info.block_hour
“
联表查询
大部分情况下我们需要的数据不在同一张表中,比如transactions表存的只有transaction的数据,没有代币币价的数据。如果我们希望能够计算出transaction对应的USD 价值,那就需要用联表查询把价格的数据给关联进来
联表查询可以理解为把两张表通过一定的条件关联起来形成一张虚拟的表,你可以方便的对虚拟表做更多的处理
联表查询由两个部分组成
联表方式(join,left join rigth join , cross join ,full join)
关联条件(on)多个条件用 and 连接
下面是关联条件的结构图
left join:以左表为主,把右表按照关联条件(on)往左表去关联,如果关联不到就用null填充
Table A 跟 Table B通过姓名关联,因为是以左表为主,所以尽管左表中lucy和在右表中没有符合关联条件的数据,但是lucy会出现在结果中,右表那部分因为关联不到数据,因此都用null填充
SQL
“
with transaction_info as -- 通过with as 建立子查询并且命名为 transaction_info
(
select
block_timestamp
,transactions_info.block_hour as block_hour
,from_address
,to_address
,eth_amount
,hash
,price
,eth_amount*price as usd_value
from
(
select --Select后跟着需要查询的字段,多个字段用英文逗号分隔
block_timestamp
,date_trunc('hour',block_timestamp) as block_hour --把block_timestamp用date_trunc处理成小时,方便作为主键去关联
,from_address
,to_address
,hash
,value /power(10,18) as eth_amount --通过将value除以/power(10,18)来换算精度,18是ETH代币的精度
,(receipt_gas_used*receipt_effective_gas_price*power(0.1,18)) as gas_fee
from ethereum_transactions --从 ethereum.transactions表中获取数据
where block_timestamp > date '2022-01-01' --限制Transfer时间是在2022年1月1日之后
and from_address = lower('0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296') --限制孙哥的钱包,这里用lower()将字符串里的字母变成小写格式(footprint数据库里存的模式是小写,直接从以太坊浏览器粘贴可能大些混着小写)
and value /power(10,18) >1000 --限制ETH Transfer量大于500
order by block_timestamp --基于block_timestamp做升序排列,如果想降序排列需要在末尾加desc,因为默认是asc,所以不用末尾不用加asc也行
) as transactions_info
left join
--将transactions_info与price_info的数据关联,关联方式为 left join
(
select
date_trunc('hour',timestamp) as block_hour --把timestamp用date_trunc处理成小时,方便作为主键去关联
,avg(price) as price
from
"token_price_5min"
where token_symbol = 'ETH' -- 取ETH 代币的数据
and chain ='Ethereum' -- 取以太坊上的价格数据
group by 1
) price_info
on transactions_info.block_hour = price_info.block_hour
)
select
date_trunc('day',block_timestamp) as block_date
,sum(eth_amount) as eth_amount
,sum(usd_value) as usd_value
from transaction_info -- 从子查询形成的'虚拟表', transactions_info 中取需要的数据
group by 1 -- 按照第一个字段进行分组,也可以用 date_trunc('day',block_timestamp) 进行替换
order by 1 -- 按照第一个字段进行升序排序,也可以用 date_trunc('day',block_timestamp) 或者别名block_date进行替换做升序排序
”
语法说明
子查询( with as )
通过with as 可以构建一个子查询,把一段 SQL 的结果变成一个’虚拟表’ (可以说是一个视图或者子查询),接下来的SQL中可以直接从这个’虚拟表/视图’中取数据
作用:通过 with as 可以比较好的提高SQL逻辑的可读性,避免多重嵌套
语法: with [视图名] as ( [SQL 逻辑 ]) ,多个视图用 ‘,’ 分隔
SQL
”
with out_tusd_amount as ( -- 通过with as 建立子查询并且命名为 out_usdc_amount
select
date_trunc('day',block_timestamp) as block_date
,sum(amount_raw*power(0.1,18)) as amount -- TUSD代币的精度换算
,'out_amount' as transfer_type -- 新增一列并且命名为transfer_type,值为 out_amount
from ethereum_token_transfers
where
block_timestamp >= date_add('day',-90,current_date) -- 获取最近90天的数据
and token_address = lower('0x0000000000085d4780B73119b644AE5ecd22b376') -- 筛选TUSD代币的地址的数据,TUSD的Decimals : 18,
and from_address = lower('0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296') -- 筛选从justin sun 的 地址中转出的金额
group by 1
)
,in_tusd_amount as ( -- 建立子查询并且命名为 in_TUSD_amount
select
date_trunc('day',block_timestamp) as block_date
,sum(amount_raw*power(0.1,18)) as amount -- TUSD代币的精度换算
,'in_amount' as transfer_type -- 新增一列并且命名为transfer_type,值为in_amount**
**
from ethereum_token_transfers
where
block_timestamp >= date_add('day',-90,current_date) -- 筛选最近90天的数据
and token_address = lower('0x0000000000085d4780B73119b644AE5ecd22b376') -- 筛选TUSD代币的地址的数据,USDC的Decimals : 18,
and to_address = lower('0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296') -- 筛选从justin sun 的 地址中转入的金额
group by 1
)
select
block_date
,transfer_type
,sum(amount) as amount
from (
select * from out_tusd_amount
union all
select * from in_tusd_amount
) as un
group by 1,2 -- 根据第一个和第二个字段进行分组,即block_date和transfer_type 两个字段
order by 1
“
语法说明
合并表 (union, union all)
用法:UNION 操作符用于合并两个或多个 SELECT 语句的结果集
请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。
语法
SELECT column_name(s) FROM table_name1
UNION ALL
SELECT column_name(s) FROM table_name2
推荐阅读
#Footprint Analytics for SQL
Footprint Analytics 是首家 Crypto 领域支持无代码数据分析平台。平台还提供一个统一的数据 API,让用户可以快速检索超过23条公链生态的 NFT,GameFi 以及 DeFi 数据。
如果您对该课程有任何反馈或建议,您可以通过以下方式联系我们。
Footprint Website: https://www.footprint.network
Discord: https://discord.gg/3HYaR6USM7
Twitter: https://twitter.com/Footprint_Data