OCCI-SQL中'>'表现为'>='及occi::Number类型转换异常问题
问题描述
我在Linux环境使用OCCI 21版本,执行以下查询时,传入表中max值作为参数,预期无结果,但却返回了该max值对应的行:
select * from CacheMessages where message_id > $message_id_p order by message_id, message_number
而在另一张表执行类似查询时结果正确(无返回):
select * from Bank where bank_obj_num > $bank_obj_num
已知条件:
- 无舍入问题(均为整数值);
- 数据量少(3500条);
- 两个查询的参数均为int64类型(约1,260,364,000,000);
- CacheMessages表有一个由触发器填充的字段(非查询条件字段)。
附CacheMessages表结构及触发器代码:
CREATE TABLE "CACHE_MESSAGES" ( "MESSAGE_OBJ_NUM" NUMBER NOT NULL ENABLE, "MESSAGE_TIME" DATE, "MESSAGE_NUMBER" NUMBER, "MESSAGE_ID" NUMBER, "SERIAL_ID" NUMBER, "MESSAGE" VARCHAR2(4000 CHAR), CONSTRAINT "CACHE_MESSAGES_PK" PRIMARY KEY ("MESSAGE_OBJ_NUM", "MESSAGE_TIME") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE( BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) LOCAL (PARTITION "CACHE_MESSAGES_MAXV" PCTFREE 10 INITRANS 2 MAXTRANS 255 LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "TBS_RNDSAN65" ) ENABLE ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE( BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "TBS_RNDSAN65" PARTITION BY RANGE ("MESSAGE_TIME") (PARTITION "CACHE_MESSAGES_MAXV" VALUES LESS THAN (MAXVALUE) SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 8388608 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "TBS_RNDSAN65" ) ; CREATE INDEX "CACHE_MESSAGES_IND_01" ON "CACHE_MESSAGES" ("SERIAL_ID") PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE( BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) LOCAL (PARTITION "CACHE_MESSAGES_MAXV" PCTFREE 10 INITRANS 2 MAXTRANS 255 LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "TBS_RNDSAN65" ) ; CREATE OR REPLACE TRIGGER "CACHE_MESSAGES_SERIAL_TRG" BEFORE INSERT OR UPDATE ON CACHE_MESSAGES REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW DECLARE iCounter cache_messages.serial_id%TYPE; cannot_change_counter EXCEPTION; BEGIN IF INSERTING THEN Select cache_messages_serial_seq.NEXTVAL INTO iCounter FROM Dual; :new.serial_id := iCounter; END IF; IF UPDATING THEN IF NOT (:new.serial_id = :old.serial_id) THEN RAISE cannot_change_counter; END IF; END IF; EXCEPTION WHEN cannot_change_counter THEN raise_application_error(-20000, 'Cannot Change Counter Value'); END;
编辑补充
问题根源似乎出在occi::Number:由于绑定变量是大数值,我使用occi::Number支持的最大类型long double,测试发现异常:将1260365696006存入occi::Number后,转为unsigned long输出得到1260365696005,数值不一致!测试代码如下:
long double ld1 = (long double)1260365696006; oracle::occi::Number num2(ld1); LOG("num2=%.9Lf num2(unsigned long)=%ld", (long double)num2, (unsigned long)num2); // 输出结果: // num2=1260365696006.000000000 num2(unsigned long)=1260365696005
问题分析与解决
核心原因
查询异常的本质是**occi::Number与long double/unsigned long之间的类型转换精度丢失**:
- 大整数
1260365696006存入occi::Number后,转换为unsigned long时出现向下取整错误,导致实际传入SQL的参数值比预期小1。 - 这直接解释了
message_id > $message_id_p返回max值行的原因:参数实际为max_value - 1,满足max_value > max_value - 1的条件。
为什么另一张表查询正常?
可能是Bank表的bank_obj_num字段值/参数值未触发该转换精度问题,或者该字段采用了直接整数类型绑定而非long double作为中间类型。
解决方法
- 规避
long double中间类型:对于大整数,直接使用occi::Number的整数安全构造方式:// 方式1:字符串构造,彻底避免浮点精度问题 oracle::occi::Number num2("1260365696006"); // 方式2:直接传入int64_t类型(OCCI 21版本支持该类型绑定) int64_t val = 1260365696006; oracle::occi::Number num2(val); - 匹配绑定类型:确保SQL参数绑定的类型与数据库
NUMBER字段完全匹配,减少不必要的类型转换环节。 - 提前验证转换结果:在绑定参数前,验证
occi::Number转换回整数的结果是否与原始值一致,避免错误参数传入SQL。
内容的提问来源于stack exchange,提问作者Arnon
相关产品推荐
相关产品推荐

