(已解决) 非等值 join 和最近邻匹配

常见的连接计算一般都是等值 join,论坛中写了很多关于等值 join 的最佳实践。但也有一些业务场景是基于时间序列的数据关联分析属于非等值 join,因为时间总是不完全对齐的,总有一些原因像网络延迟、硬件性能、或者人为干预等可导致某些事件状态存在时间上的偏差。比如某些交易价格,行情变化很快,单位时间内会变化多次,要分析交易执行价格跟交易实际发生那一刻的价格有何差异,以便于计算滑点,还有像一些状态分析,获取某种状态之前那一刻是什么状态等。

举个简单的例子,比如有以下两个表,左表是关于一些材料的信息,需要从右表中找到最接近左表年月字段的一些信息,只需返回一条记录:

左表:

imagepng

右表:

imagepng

生成的结果如下:

imagepng

可以看到返回的最近年月有相等的也有不等的。

这种问题如果用 SQL 解决,可以用窗口函数或者 lateral join:

--方法1:窗口函数
with state as (
    select
        材料代码, 年月, 材料名称, 材料费, 加工费, 总成本, 来源,
        lead(年月, 1, 300000) --默认一个较大的数作为年月,此处的年月是整型
            over (partition by 材料代码 order by 年月) as 结束
    from 右表
)
select z.材料代码, z.年月, s.年月 最近年月, s.* exclude (年月,材料代码,结束)
from 左表 z
left join state s
        on z.材料代码 = s.材料代码
      and z.年月 >= s.年月
      and z.年月 < s.结束;
      
--方法2: left join lateral 
select z.*, b.*
from 左表 z
left join lateral (
    select 年月 最近年月, * exclude (材料代码,年月)
    from 右表 y
    where z.材料代码 = y.材料代码
      and z.年月 >= y.年月
    order by y.年月 desc
    limit 1
) b on true;

也有专门用于这种不等值关联的 asof join 语句,结果只返回一条满足条件的记录:

--方法3: asof join
select 
    z.材料代码,z.年月,y.年月 最近年月, y.* exclude (材料代码,年月)
from 
    左表 z
asof left join
    右表 y
on
    z.材料代码 = y.材料代码
    and
    z.年月 >= y.年月;

如果用集算器来处理这种 IEjoin 和最近邻匹配,是不是只能用 xjoin/xjoinx 来实现:

imagepng

可以看到在 xjoin 里可以启用选项 @z1,表示从后往前取 1 条,这样就不会返回所有叉乘了,而最左边的 @1 选项可以控制在找不到记录时返回 null,这个很完美,完美解决了最近领的问题。

但如果用 xjoinx 涉及到数据量稍大要用游标来处理的时候,xjoinx 里中表的选项 @zob1 就会失效,会返回所有叉乘结果。

大佬们有空时可否指导一下:

1、这种 IEjoin 最近邻匹配,集算器方法的最佳实践是什么样的? 数据量不大时,xjoin 完全能胜任,选项 @z1 非常好用,@b 时条件不怎么好写,经常搞不对。当稍大数据量时,往往是右表稍大的场景多一些,要如何处理,xjoinx 返回的是所有叉乘,而最近邻匹配只需返回 1 条;

2、xjoin 很强,我觉得完全不亚于 group 函数,特别是表的过滤选项强的很,但 @1zob 四个选项并没有明确记录在官方文档中,我之前是从 IDE 的 ALT+↓显示的函数说明中发现的,现在也被移除了,但目前还能用。其中的 z 表示从后往前找,数字 1 表示只返回 1 条记录,找到就立即返回并停止继续查找,b 表示使用二分法查找,但选项 o 不知道是什么作用,琢磨不出来。这些选项很好用的,还能明确启用吗?

恳请大佬们有空时谈一下集算器非等值 join 的最佳实践,论坛中几乎没有 IEjoin 的案例文章,而实务中这种 IEjoin 和最近领匹配还是常见的。