esProc SPL 处理多层 JSON 数据要比 DuckDB 方便很多,尤其需要保留 JSON 层次以及进行复杂计算时。
DuckDB 的 JSON 处理能力还是不错的,read_json_auto() 能直接把 JSON 解析成表格结构,直接对着多层数据操作就行:
SELECT order_id, order_date, json_extract(customer, '$.name') AS cusName,json_extract(customer, '$.city') AS cusCity FROM read_json_auto('orders.json')
SPL 做这种基本运算会更简单:
json(file("orders.json").read()).new(order_id, order_date,customer.name:cusname,customer.city:cuscity)
直接用点(.)取子层级数据,很直观。
稍复杂的运算,比如要计算某个订单数据里的 Electronics 分类的销售金额。用 DuckDB 需要展开 order_details,再筛选 category=‘Electronics’,然后求个 SUM(price * quantity)。
SELECT sum(od.quantity*od.price) amount
FROM read_json_auto('orders.json') AS o,
LATERAL UNNEST(o.order_details) AS t(od),
LATERAL UNNEST([od.product]) AS t(p)
WHERE p.category = 'Electronics'
为了完成这样的计算,SQL 要把子表和主表关联起来做内接连来实现过滤,已经有点绕了,但还是不算非常复杂。
SPL 则可以直接将子表当集合运算:
json(file("order3.json").read()).conj(order_details).select(product.category=="Electronics").sum(quantity*price)
一句写完, 不需要做关联,逻辑简单,比 DuckDB 的优势就更明显了。
情况再复杂点,比如要先筛选 Electronics 类的订单明细,再剔除订单金额低于 200 的订单,DuckDB 的 SQL 就开始变得难写了。展开 order_details 之后要再聚合计算订单金额,然后再基于这个结果筛选符合条件的订单,再做嵌套查询或者用 CTE 才能保持数据结构的完整性,SQL 一长,调试起来就不太友好了。用 lambda 语法能简单一些,但和传统 SQL 形式大相径庭了。
SELECT
o.order_id,
LIST_FILTER(o.order_details, x -> x.product.category = 'Electronics') AS order_details
FROM read_json_auto(orders.json') AS o
WHERE
ARRAY_LENGTH(LIST_FILTER(o.order_details, x -> x.product.category = 'Electronics')) > 0
AND SUM(
LIST_FILTER(o.order_details, x -> x.product.category = 'Electronics') ->
(x -> x.price * x.quantity)
) > 200;
SPL 代码依然很自然:
=A2.select(order_details.select@1(product.category=="Electronics") && order_details.sum(price*quantity)>200)
还是一句,将子表作为集合处理就行了。不涉及烧脑的子查询和 Lambda 语法,不管多少层直接引用,过滤、聚合直接整就完了。而且 SPL 还能保持 JSON 的多层结构,不需要折腾 GROUP BY 和 LATERAL UNNEST 之类的复杂 SQL。
DuckDB 确实对 JSON 处理得不错,但写起来还是要倒腾 UNNEST 之类的 SQL 结构,层次一多就显得麻烦。而 SPL 直接按 JSON 的多层结构处理,既能方便筛选、聚合,又能保持数据的原始层次,显然更适合应对复杂 JSON 计算场景。
esProc SPL是开源免费的,欢迎前往乾学院了解更多!