PLS_INTEGER何时能切实提升性能?与DATE运算为何更高效?
问题背景
原本认为PLS_INTEGER仅在与同类型做算术运算时才有性能优势,但在Oracle 19.0.0环境下的测试显示,PLS_INTEGER在与DATE类型的运算中,性能表现明显优于Number类型,测试代码如下:
测试代码1:结合add_months的场景
declare i integer; v_date_r date; v_months /*pls_integer*/ number := 7; v_days /*pls_integer*/ number := 98; begin for i in 1..10000000 loop v_date_r := add_months(trunc(localtimestamp),-v_months) + v_days; end loop; end;
测试代码2:直接DATE加天数的场景
declare i integer; v_date_r date; v_days /*pls_integer*/ number := 322; begin for i in 1..10000000 loop v_date_r := sysdate + v_days; end loop; end;
核心疑问:sysdate + v_days操作中,按理解PLS_INTEGER会转成Number,但实际性能却更快;add_months接受Integer类型,传入PLS_INTEGER比Number更快虽合理,但官方文档未明确说明原因。
原因解析
1. PLS_INTEGER的底层特性
PLS_INTEGER是Oracle专为PL/SQL设计的32位原生整数类型,它的存储和运算直接使用硬件的整数指令,不需要像Number类型那样处理可变精度的十进制格式,内存开销和运算耗时都远低于Number。
2. add_months函数的参数优化
虽然官方文档标注add_months的第二个参数为INTEGER,但Oracle里的INTEGER本质是NUMBER(38)的别名。当传入PLS_INTEGER时,由于它本身就是原生整数,几乎不需要类型转换就能被函数直接使用;而传入Number类型时,Oracle需要先把十进制格式的Number转成函数内部预期的整数格式,千万次循环的累计转换开销,就造成了明显的性能差距。
3. DATE + 整数的底层运算逻辑
DATE类型在Oracle内部是用7字节的整数结构存储的(分别存储世纪、年、月、日、时、分、秒)。当执行DATE + 整数的操作时:
- 如果是PLS_INTEGER类型的天数,Oracle会直接将这个原生整数与DATE内部存储的“日”字段做整数运算,完全跳过Number类型的转换步骤;
- 如果是Number类型的天数,Oracle必须先把十进制的Number转成整数,再执行DATE的内部运算,这个额外的转换步骤在循环密集型操作中会被放大,导致性能变慢。
补充说明
Oracle官方文档通常不会过多披露这类底层执行优化的细节,因为这属于版本相关的实现逻辑,但在19c及后续版本中,PLS_INTEGER的原生整数优势在循环次数多的场景下,确实能带来显著的性能提升。
内容的提问来源于stack exchange,提问作者ORA-1403

