WHERE子句日期比较报错:数据类型不匹配问题排查
你遇到的ERROR: Expression using equals (=) has components that are of different data types错误,核心原因是数据类型不匹配:你的ACTIVITY_DATE是SAS日期型变量(本质是数值,代表从1960年1月1日起的天数),但你通过PUT函数把INTNX生成的日期转成了字符型字符串,随后在WHERE子句里直接用字符和日期型变量比较,SAS自然会报错。
下面给你三种可行的解决方案,按推荐程度排序:
方法一:宏变量存储SAS日期数值(最推荐)
跳过把日期转成字符的步骤,直接将INTNX生成的SAS日期数值(纯数字)存入宏变量,这样在WHERE子句里就能直接和日期型变量比较,类型完全匹配:
DATA _NULL_; CALL SYMPUT('MonthStart', INTNX('MONTH', TODAY(), 0, 'B')); CALL SYMPUT('MonthEnd', INTNX('MONTH', TODAY(), 0, 'E')); RUN; %PUT &MonthStart.; /* 输出的是SAS日期数值,比如23100这类数字 */ %PUT &MonthEnd.; PROC SQL; CREATE TABLE CURRENT_MONTH_ACTIVITY AS SELECT * FROM MONTH_END_BASE WHERE ACTIVITY_DATE >= &MonthStart. AND ACTIVITY_DATE <= &MonthEnd. ; QUIT;
注意这里宏变量不需要加引号,因为我们传递的是数值,和日期型变量的本质一致。
方法二:将字符宏变量转成日期型再比较
如果你一定要保留字符格式的日期宏变量,可以在WHERE子句里用INPUT函数把字符串转回SAS日期型,和ACTIVITY_DATE类型统一:
DATA _NULL_; CALL SYMPUT('MonthStart', TRIM(PUT(INTNX('MONTH', TODAY(), 0, 'B'), ddmmyys10.))); CALL SYMPUT('MonthEnd', TRIM(PUT(INTNX('MONTH', TODAY(), 0, 'E'), ddmmyys10.))); RUN; %PUT &MonthStart.; %PUT &MonthEnd.; PROC SQL; CREATE TABLE CURRENT_MONTH_ACTIVITY AS SELECT * FROM MONTH_END_BASE WHERE ACTIVITY_DATE >= INPUT('&MonthStart.', ddmmyys10.) AND ACTIVITY_DATE <= INPUT('&MonthEnd.', ddmmyys10.) ; QUIT;
INPUT函数会按照指定的格式(这里是ddmmyys10.)把字符字符串转换成SAS日期数值,这样两边类型就匹配了。
方法三:直接在WHERE子句使用INTNX函数(最简洁)
其实完全可以省去宏变量的步骤,直接在PROC SQL的WHERE子句里计算当月的起止日期,代码更简洁,也彻底避免类型问题:
PROC SQL; CREATE TABLE CURRENT_MONTH_ACTIVITY AS SELECT * FROM MONTH_END_BASE WHERE ACTIVITY_DATE BETWEEN INTNX('MONTH', TODAY(), 0, 'B') AND INTNX('MONTH', TODAY(), 0, 'E'); QUIT;
这种方式下,SAS会直接计算出当月的起始和结束日期(都是日期型数值),和ACTIVITY_DATE直接比较,完全不会有类型冲突。
小提示
SAS的日期型变量本质是数值,所以日期比较的关键是两边类型一致:要么都是SAS日期数值,要么都是通过转换得到的同类型日期值,千万不要直接用字符和日期型变量比较哦。
内容的提问来源于stack exchange,提问作者sivaranjani manoharan

