SAS WHERE子句中使用INDEXW函数逻辑异常问题咨询
SAS WHERE子句INDEXW逻辑判断异常根因分析
问题复现代码
data testdata; length var1 var2 $ 10; var1 = "house"; var2 = ""; output; var1 = "house"; var2 = "car"; output; var1 = "house"; var2 = "house"; output; run; proc sql; * 正常返回3条观测; create table try1 as select * from testdata where indexw("house", var1); * 正常返回1条观测; create table try2 as select * from testdata where indexw("house", var2); * 正常返回1条观测; create table try3 as select * from testdata where indexw("car", var2); * 异常返回3条观测,预期仅返回var2="house"的1条; create table try4 as select * from testdata where indexw("house", var1) and indexw("house", var2); * 正常返回1条观测; create table try5 as select * from testdata where indexw("house", var1) and indexw("car", var2); * 显式比较返回值,正常返回1条观测; create table try6 as select * from testdata where indexw("house", var1) and (indexw("house", var2) ne 0); * 调换条件顺序,正常返回1条观测; create table try7 as select * from testdata where indexw("house", var2) and indexw("house", var1); quit;
对应DATA步测试现象:
* WHERE语句异常返回3条观测 data t; set testdata; where indexw("house", var1) and indexw("house", var2); run; /* 日志输出重写后的WHERE逻辑:WHERE not (not INDEXW('house', var1)); */ * IF语句正常返回1条观测 data t; set testdata; if indexw("house", var1) and indexw("house", var2); run;
根本原因
该异常是SAS 9.4 TS1M7及更早版本中,WHERE子句优化器的布尔表达式重写逻辑存在缺陷导致的:
- WHERE子句在执行前会经过SAS优化器的自动化简,核心目的是提取公共表达式、消除冗余判断,提升执行效率;而DATA步的IF子集语句不会走这套优化流程,会严格按照书写的逻辑逐行执行,因此IF语句结果始终符合预期。
- 当AND连接的两个判断同时满足以下三个条件时,会触发优化器的判定bug:
- 两个判断调用了完全同名的函数(本次案例中均为
indexw) - 函数的第一个传入参数是完全相同的常量(本次案例中首参数均为字符串常量
"house") - 直接将函数返回的数值作为布尔判断依据(SAS默认数值0为假、非0为真,没有显式写
ne 0/>0这类比较逻辑)
- 两个判断调用了完全同名的函数(本次案例中均为
- 触发bug后,优化器会错误判定两个函数调用是完全等价的冗余表达式,直接做逻辑消除,仅保留第一个判断条件。从DATA步的日志可以看到,原本写的
indexw("house", var1) and indexw("house", var2)被错误重写为not (not INDEXW('house', var1)),也就是等价于只判断indexw("house", var1),第二个关于var2的判断被完全丢弃,因此才会返回全部3条var1="house"的观测。
各正常用例的原理
所有返回符合预期结果的测试用例,本质都是破坏了上述bug的触发条件,让优化器无法执行错误的化简:
try5:第二个indexw的首参数为"car",和第一个函数的首参数不一致,不会被判定为重复表达式try6:第二个indexw加了显式的ne 0比较,不再是直接将函数返回值作为布尔条件,不会触发对应化简规则try7:调换两个判断的书写顺序后,没有命中优化器的错误化简分支,因此不会错误消除条件
规避方案
在未安装对应修复补丁的环境中,可通过以下任意方式绕开该问题:
- 所有布尔场景下调用返回数值的函数时,显式写出比较逻辑,例如将
indexw(a,b)写为indexw(a,b) ne 0,避免直接把数值当布尔值传入WHERE子句 - 调整条件书写顺序,避开优化器的错误判定分支
- DATA步中需要做子集筛选时优先用IF语句,不使用WHERE语句
- 安装SAS官方发布的对应版本热修复补丁,该优化器判定缺陷在后续更新中已被修复。
内容的提问来源于stack exchange,提问作者Chris Long
相关产品推荐
相关产品推荐

