子查询WHERE条件放置位置不同导致SQL查询结果不一致
两段SQL结果不一致的根因
两段SQL返回条数差410的核心原因非常明确:row_number()窗口函数的计算范围,受筛选条件位置的直接影响,两种写法下参与行号排序的数据集根本不是同一批。
两种写法的执行逻辑差异
SQL的执行顺序遵循FROM/JOIN -> WHERE过滤 -> 窗口函数计算 -> SELECT -> 外层过滤的规则,两种写法的执行路径完全不同:
第一段SQL(返回15506条)
- 先对T1表做过滤,只保留
event_type="OS"、product="pt"、ts_est在2021-10-05到2022-03-07区间的记录 - 用这批过滤后的T1记录左关联T2表
- 对关联后的结果,按
userid+product+event_type分组,按bornondate倒序计算row_number - 最后取rowid=1的记录返回
这个逻辑下,所有符合筛选条件的记录都会参与行号排序,每个分组里最新的那条一定会拿到rowid=1,不会丢数。
- 先对T1表做过滤,只保留
第二段SQL(返回15096条)
- 不对T1做任何过滤,直接拿T1全表左关联T2表
- 对全量关联结果按相同分组规则计算row_number,不管记录是否符合目标时间、产品、事件类型条件
- 先取全量数据里rowid=1的记录
- 最后才在外层过滤,留下符合时间、产品、事件类型的记录
这个逻辑下,只要某个用户存在一条不符合筛选条件(比如时间在2022-03-07之后、产品不是pt、事件类型不是OS)的记录,且这条记录的bornondate比该用户所有符合条件的记录都新,这条不符合条件的记录就会抢走rowid=1的位置。等外层过滤时,这条rowid=1的记录因为不符合条件被筛掉,而该用户真正符合条件的记录因为rowid不等于1,根本进不到最终结果里,就产生了丢数。
差异数据验证
你提到差异记录的userid在两张表都存在,完全符合上面的逻辑:这些用户都有至少一条不符合筛选条件的T1记录,在全量计算row_number时抢占了行号1的位置。你可以执行下面的SQL直接定位到抢行号的记录,返回的条数正好是410:
SELECT userid, product, event_type, ts_est FROM ( SELECT ro.userid, ro.product, ro.event_type, ro.ts_est, row_number() OVER( PARTITION BY ro.userid, ro.product, ro.event_type ORDER BY fa.bornondate DESC ) AS rowid FROM T1 ro LEFT JOIN T2 fa ON ro.userid = fa.userid ) t WHERE rowid = 1 AND NOT ( CAST(ts_est AS DATE) BETWEEN '2021-10-05' AND '2022-03-07' AND product = "pt" AND event_type = "OS" )
写法选择
如果你的业务需求是「在指定时间范围、pt产品、OS事件的记录中,取每个用户最新bornondate对应的申请记录」,第一段SQL的写法是正确的,第二段写法因为把筛选放在窗口函数计算之后,会导致范围外数据干扰行号计算,出现丢数。
内容的提问来源于stack exchange,提问作者swati
相关产品推荐
相关产品推荐

