前言

这篇文章可能是你至今(2022-02-11)能在互联网看到的,关于utl_raw包的逻辑说得最深入的一篇文章了。

由于最近在复刻oracle中自带的包到其他数据库,因此需要对oracle中的包的逻辑进行解析。
比如UTL_RAW这个包,以前用得挺多,但没深究其函数逻辑,这次仔细分析,发现了有一些函数涉及到了一些计算机基本原理及IEEE标准,比较有意思,因此写一篇这样的文章来分享。

函数清单

先上官方文档 UTL_RAW

再贴个函数清单,稍微翻译了下,方便理解

Subprogram Description 中文描述
BIT_AND Function Performs bitwise logical “and” of the values in RAW r1 with RAW r2 and returns the “anded” result RAW 计算两个raw的“位与”并返回结果的RAW
BIT_COMPLEMENT Function Performs bitwise logical “complement” of the values in RAW r and returns the “complement’ed” result RAW 对 in 中的值执行按位逻辑“补码”RAW r并返回“补码”结果RAW
BIT_OR Function Performs bitwise logical “or” of the values in RAW r1 with RAW r2 and returns the “or’d” result RAW 计算两个raw的“位或”并返回结果的RAW
BIT_XOR Function Performs bitwise logical “exclusive or” of the values in RAW r1 with RAW r2 and returns the “xor’d” result RAW 计算两个raw的“位异或”并返回结果的RAW
CAST_FROM_BINARY_DOUBLE Function Returns the RAW binary representation of a BINARY_DOUBLE value 返回值的二进制 RAW表示的双精度浮点
CAST_FROM_BINARY_FLOAT Function Returns the RAW binary representation of a BINARY_FLOAT value 返回值的二进制 RAW表示的单精度浮点
CAST_FROM_BINARY_INTEGER Function Returns the RAW binary representation of a BINARY_INTEGER value 返回值的二进制 RAW表示的整数
CAST_FROM_NUMBER Function Returns the RAW binary representation of a NUMBER value 返回值的二进制 RAW表示的数值
CAST_TO_BINARY_DOUBLE Function Casts the RAW binary representation of a BINARY_DOUBLE into a BINARY_DOUBLE 将一个RAW二进制表示形式的双精度浮点转换为 双精度浮点数字
CAST_TO_BINARY_FLOAT Function Casts the RAW binary representation of a BINARY_FLOAT into a BINARY_FLOAT 将一个RAW二进制表示形式的单精度浮点转换为 单精度浮点数字
CAST_TO_BINARY_INTEGER Function Casts the RAW binary representation of a BINARY_INTEGER into a BINARY_INTEGER 将一个RAW二进制表示形式的整数转换为 数字
CAST_TO_NUMBER Function Casts the RAW binary representation of a NUMBER into a NUMBER 将一个RAW二进制表示形式数值转换为 成一个数值
CAST_TO_NVARCHAR2 Function Converts a RAW value into a VARCHAR2 value 将RAW值转换为NVARCHAR2值
CAST_TO_RAW Function Converts a VARCHAR2 value into a RAW value 将VARCHAR2值转换为RAW值
CAST_TO_VARCHAR2 Function Converts a RAW value into a VARCHAR2 value 将 RAW 值转换为VARCHAR2值
COMPARE Function Compares RAW r1 against RAW r2 比较两个raw
CONCAT Function Concatenates up to 12 RAWs into a single RAW 最多可将 12 个连接RAWs成一个RAW
CONVERT Function Converts RAW r from character set from_charset to character set to_charset and returns the resulting RAW 将一个raw从字符集from_charset转换为字符集to_charset并返回结果RAW
COPIES Function Returns n copies of r concatenated together 返回n个重复的raw拼接的结果
LENGTH Function Returns the length in bytes of a RAW r 返回一个RAW的字节长度
OVERLAY Function Overlays the specified portion of target RAW with overlay RAW, starting from byte position pos of target and proceeding for len bytes 使用指定的RAW,对目标raw的pos字节位置开始,len字节长度进行覆盖
REVERSE Function Reverses a byte sequence in RAW r from end to end 反转RAW字节序列
SUBSTR Function Returns len bytes, starting at pos from RAW r 对一个raw,从pos位置开始,截取len字节,返回结果
TRANSLATE Function Translates the bytes in the input RAW r according to the bytes in the translation RAWs from_set and to_set 对于输入的raw, 将from_set中的字节逐个替换成to_set中对应位置的字节
TRANSLITERATE Function Converts the bytes in the input RAW r according to the bytes in the transliteration RAWs from_set and to_set 实际上和translate差不多,但多了个补充长度的参数
XRANGE Function Returns a RAW containing all valid 1-byte encodings in succession, beginning with the value start_byte and ending with the value end_byte 从start_byte开始到end_byte的所有值拼接成一个raw返回

此包中的大部分函数,均已使用openGauss的plpgsql语言进行了实现,可结合代码来阅读本文,代码地址
https://gitee.com/darkathena/opengauss-oracle/blob/main/oracle-package/utl_raw.sql

详解

注意,由于oracle中,sql语言可以隐式的将十六进制字符串转换成raw类型,所以注意下文中十六进制的参数的类型应该均为raw。在plsql中,则需要使用hextoraw函数进行显式的转换以确保输入参数的准确性

1.BIT_AND

作用:
计算位与
例:

SELECT UTL_RAW.BIT_AND('F0','BA') FROM DUAL
--输出:B0

算法:
十六进制 F0 转二进制 11110000
十六进制 BA 转二进制 10111010
两个数字按位,逐个进行匹配,相等输出1,不等输出0
得10110000,然后转十六进制,得B0

2.BIT_OR

作用:
计算位或
例:

SELECT UTL_RAW.BIT_OR('F0','BA') FROM DUAL
--输出:FA

算法:
十六进制 F0 转二进制 11110000
十六进制 BA 转二进制 10111010
两个数字按位,逐个进行匹配,至少有个1则输出1,都是0则输出0
得11111010,然后转十六进制,得FA

3.BIT_XOR

作用:
计算位异或
例:

SELECT UTL_RAW.BIT_XOR('F0','BA') FROM DUAL
--输出:4A

算法:
十六进制 F0 转二进制 11110000
十六进制 BA 转二进制 10111010
两个数字按位,逐个进行匹配,相等输出0,不等输出1
得01001010,然后转十六进制,得4A

4.BIT_COMPLEMENT

作用:
计算补码
例:

select UTL_RAW.BIT_COMPLEMENT('EA') FROM DUAL
--输出:15

算法:
EA转二进制 11101010
用二进制 11111111减去它(或者理解为1变0,0变1),
得二进制 00010101,再转十六进制,得15

5.CONCAT

作用:
拼接多个raw,最多拼12个
例:

select UTL_RAW.CONCAT('EAAB','3A') FROM DUAL
--输出 :EAAB3A

算法:
EAAB 转二进制 1110101010101011
3A 转二进制 00111010
计算第二个参数,字节长度为1,占8位,因此第一个参数的二进制向左移动8位,
得111010101010101100000000,
然后加上第二个参数的2进制,得 111010101010101100111010,转十六进制得 EAAB3A
(可以看到,此例如果是把十六进制数值当成字符串来进行拼接,结果是一样的,但要注意是按字节长度拼接,比如 “FF"拼"F”,得到应该是 “FF0F”,而不是字符串拼接的"FFF")

6.LENGTH

作用:
计算长度,单位:字节
例:

select UTL_RAW.LENGTH('EAEA') FROM DUAL
--输出:2

算法:
EAEA转二进制 1110101011101010
从后往前数,8位为1个字节,不足8位的前面补0
(可以将十六进制数值视同为字符串,该字符串长度的一半即为原数据的字节长度,但同样要注意省去了前面的0的情况,比如"FFF"按字符串的长度是1.5,明显不对,应该计算"0FFF"长度的一半)

7.SUBSTR

作用:
按字节截取
例:

select UTL_RAW.SUBSTR('ABCDEF12345',3,2) FROM DUAL
--输出:EF12

算法:
(转二进制过程略)
AB 为一个字节, CD为一个字节,从第3字节 EF 开始,截取 2个字节长度,即 EF 12
(和字符串的substr类似,可以理解为2个字符一组,第3个参数为空时表示一直截到最后;第2个参数同样支持负数,即从倒数第n位开始截;但是第3个参数要截的长度不能超过raw的剩余长度,否则会报错ora-06502)

8.COPIES

作用:
复制自己成多份
例:

select utl_raw.COPIES('1A1B',3) FROM DUAL
--输出:1A1B1A1B1A1B

算法:
(转二进制过程略)
1A1B字节长度为2,往左移两个字节长度,得1A1B0000
再加上自己,得1A1B1A1B,继续左移两个字节长度,得1A1B1A1B0000
再加上自己,得1A1B1A1B1A1B
可以发现左移次数为第2个参数减1次
(也可以简单的视为字符串拼接,但同样要注意前面补0,比如utl_raw.COPIES(‘FFF’,2) 的结果应该是0FFF0FFF)

9.REVERSE

作用:
按字节进行翻转
例:

select utl_raw.REVERSE('01020304') from dual
--输出:04030201

算法:
(转二进制过程略)
01020304字节长度为4,
取第4个字节04,左移3个字节,得04000000
取第3个字节03,左移2个字节,得00030000
取第2个字节02,左移1个字节,得00000200
取第1个字节01,得00000001
把上面4个数加起来,得04030201
(也可以简单的视为字符串,只不过是2个字符为一组进行翻转,不要忘了补0)

10.XRANGE

作用:
根据传入的两个参数作为开始数值和结束数值,以1递增,生成一串序列,注意两个参数均只接受第一个字节,后面的字节会被忽略
例:

select utl_raw.XRANGE('0A','12') FROM DUAL
--输出:0A0B0C0D0E0F101112

算法:
(转二进制过程略)
0A左移一个字节,得0A00,然后0A加1,得0B,0A00加上0B得0A0B
0A0B左移一个字节,得0A0B00,然后0B加1得0C,0A0B00加上0C得0A0B0C
循环下去,直至中间某次加1后的值等于第二个参数12,加上最后一个值后返回结果,
得0A 0B 0C 0D 0E 0F 10 11 12

11.TRANSLITERATE

作用:
按字节进行替换,(由于第二个参数和第三个参数的长度允许不一致,因此以填充码的方式,确保原数据和输出结果的长度一致)
例:

select utl_raw.transliterate( '010203040502', '0809', '01020304', '0a' ) FROM DUAL
--输出:08090A0A0509

算法:
(转二进制过程略)
第一个参数是原数据,第二个参数是要替换成的数据,第三个参数是要查找的数据,第4个参数是填充码
此例中,对第一个参数进行以下字节的覆盖
01 -> 08
02 -> 09
03 -> 空(取填充码0A)
04 -> 空(取填充码0A)
得输出结果 08 09 0A 0A 05 09

12.TRANSLATE

作用:
按字节进行替换(无填充码参数,因此输入和输出的长度可能不一致)
例:

select utl_raw.translate( '0102030405', '0203', '06' ) from dual
--输出 01060405

算法:
和TRANSLITERATE函数类似,不过没有填充码,没对应上的则替换成空(二进制数据需要右移),而且注意第二个参数和第三个参数反过来了

13.OVERLAY

作用:
按指定开始字节及长度覆写字节数据
用法:
第一个参数为覆写码,第二个参数为原始数据,第3个参数为开始字节位置(默认值为1,表示从第一个字节开始),第4个参数为覆盖字节长度(默认值为剩余的所有字节长度),第5个参数为填充码(默认值为00)

select utl_raw.overlay( 'AABB', '010203' ) from dual
--输出 AABB03
select utl_raw.overlay( 'AABB', '010203', 2 ) from dual
--输出 01AABB
select utl_raw.overlay( 'AABB', '010203', 5 ) from dual
--输出 01020300AABB
select utl_raw.overlay( 'AABB', '010203', 2, 1 ) from dual
--输出 01AA03
select utl_raw.overlay( 'AABB', '010203', 5, 1, 'FF' ) from dual
--输出 010203FFAA

算法:(以最后一个为例)
(转二进制过程略)
AABB 字节长度为 2,已经超过第4个参数1个字节的长度,因此截断,只取第1个字节AA
010203 字节长度为 3
从第5个字节开始覆写,已经超过原始数据长度,差值为2,因此010203先左移1个字节,得 01020300
由于还未到第5个字节,所以加上填充码FF,得010203FF,
再左移1个字节,得010203FF00,在第5位加上覆写码AA,得010203FFAA

14.COMPARE

作用:
按字节从左至右的顺序比较两个raw的差异,返回第一个不匹配的字节位置,如果完全匹配则返回0
用法:
第一个参数raw,第二个参数raw,第3个参数为填充码(默认值为00),返回一个整数

SELECT utl_raw.compare( '010203', '01020304', '04' ) from dual
--输出 0
SELECT utl_raw.compare( '01050304', '01020304' )
--输出 2

算法:
(转二进制过程略)
第一例,由于前两个参数长度不一致,且第一个参数长度较短,因此使用第三个参数对其进行填充至相同长度,得 01020304,所以两个完全一致,返回0
第二例,可以发现第二个字节 05 不等于 02,因此返回2

15.CAST_TO_RAW

作用:
将varchar2类型的数据的二进制数据转换成raw类型
例:

select  UTL_RAW.cast_to_RAW('DarkAthena') from dual
--输出 4461726B417468656E61
select  UTL_RAW.cast_to_RAW('123') from dual
--输出 313233
select  UTL_RAW.cast_to_RAW(convert('测试','AL32UTF8')) from dual
--输出 E6B58BE8AF95

算法:
略,
注意第二例,传入的参数是字符,不是数字,
第三例中,对于非单字节字符,强制指定字符集进行写入,否则写入的raw不可控

16.CAST_TO_VARCHAR2

作用:
将raw格式的字符串使用数据库默认字符集转换回VARCHAR2(其实就是CAST_TO_RAW函数的逆向函数)
例:

select  UTL_RAW.cast_to_RAW('4461726B417468656E61') from dual
--输出 DarkAthena

算法:
略,
注意,由于此函数不能指定字符集,因此转换回来可能会乱码,可以使用utl_i18n.raw_to_char这个函数进行替代

17.CONVERT

作用:
转换RAW的字符集
例:

--先用cast_to_RAW获得一个raw
select  UTL_RAW.cast_to_RAW(convert('测试','AL32UTF8')) from dual
--输出 E6B58BE8AF95
--转换成gbk
select  UTL_RAW.CONVERT('E6B58BE8AF95','ZHS16GBK','AL32UTF8') FROM DUAL
--输出 B2E2CAD4

算法:略
可用下列方式验证

select  UTL_RAW.cast_to_RAW(convert('测试','ZHS16GBK')) from dual
--输出 B2E2CAD4

18.CAST_FROM_BINARY_INTEGER

作用:
将一个整数的二进制数据转换成RAW
用法:第1个参数为要进行转换的整数,第二个参数为大端还是小端,默认大端为1,小端为2,传3为取机器配置的端

select utl_raw.cast_from_binary_integer(65280) from dual
--输出 0000FF00
select utl_raw.cast_from_binary_integer(65280,2) from dual
--输出 00FF0000

算法:
其实就是把十进制的整数转换成其二进制数据的十六进制展现形式,但最长只能4个字节,可转换的整数范围为
-2147483648~2147483647
即大端7FFFFFFF~80000000
(7FFFFFFF及以下为正整数,80000000及以上为负整数,00000000为0,FFFFFFFF为-1)
但是要注意第2个参数,关于大小端是什么意思,可自行搜索,不过在结果展现上来看,其实小端就是把大端按字节给翻转了

19.CAST_TO_BINARY_INTEGER

作用:
将一个整数的raw转换回整数,其实就是CAST_FROM_BINARY_INTEGER的逆向函数
用法:第1个参数为要进行转换的raw,第二个参数为大端还是小端,默认大端为1,小端为2,传3为取机器配置的端

select utl_raw.cast_to_binary_integer('18FCFFFF') from dual
--输出 419233791
select utl_raw.cast_to_binary_integer('18FCFFFF') from dual
--输出 -1000

算法:
略,注意此处可以传入小于或者等于4个字节的raw,超过4个字节会报错

20.CAST_FROM_NUMBER

作用:
获得一个number类型值存储的二进制数据
例:

select utl_raw.cast_from_number(1.1) from dual
--输出 C1020B
select utl_raw.cast_from_number(-1.1) from dual
--输出 3E645B66

算法:
这里内容会比较多,目前国内很难找到关于这个算法的中文说明,用英文关键词去搜也很难找到,但是我搜到了一个CSDN上08年的帖子
关于utl_raw.cast_to_number

这位兄弟启发了我,让我能稍微看懂了一点,于是我模拟了几组数据,尝试在excel中按照此方式进行转换
从RAW解析正数

从RAW解析负数

模拟成功,但是这只是RAW到NUMBER,如果要计算NUMBER到RAW,得把这个步骤完全反过来

另外,我还根据193、101这些特殊的数字,找到了oracle的官方文档
3 Datatypes

模拟计算表格文件下载

21.CAST_TO_NUMBER

作用:
将一个number值存储的二进制数据转换回number,其实就是CAST_FROM_NUMBER的逆向函数
例:

select utl_raw.CAST_TO_NUMBER (C1020B) from dual
--输出 1.1
select utl_raw.cast_from_number(3E645B66) from dual
--输出 -1.1

算法:
略,见上面的CAST_FROM_NUMBER

22.CAST_TO_NVARCHAR2

作用:
把RAW转换回NVARCHAR2
例:

select utl_raw.CAST_TO_NVARCHAR2('6D4B8BD5') from dual
--输出:测试

算法:
其实NVARCHAR2类型就是用unicode编码来存储数据,可以使用unistr或者asciistr函数验证

select asciistr('测试') from dual
--输出: \6D4B\8BD5
select unistr('\6D4B\8BD5') from dual
--输出: 测试

但是,要注意的是,对于ascii字符集中的字符(单字节字符),NVARCHAR2存储的每个字符的字节前面都要补1个00字节,确保每个字符占的存储空间都是2个字节,比如
NVARCHAR2类型的 “测试12ab” 存储的二进制数据(十六进制形式)为 6D4B 8BD5 0031 0032 0061 0062
而asciistr函数并不会对除"\"以外的单字节字符进行转换

23.CAST_FROM_BINARY_FLOAT/CAST_TO_BINARY_FLOAT/CAST_FROM_BINARY_DOUBLE/CAST_TO_BINARY_DOUBLE

最后还剩4个函数,分别是转换单精度浮点和双精度浮点的
这个其实是遵循 IEEE二进制浮点数算术标准(IEEE 754)(百度百科)
所以暂不细说了,前面能看懂的自然能看懂百科里说的算法。
但要注意的是,由于这两种浮点数值存在精度问题,经常会出现类似10.00000000000000001或者9.9999999999999999这种奇葩的数据,因此不建议在需要准确数值的场景下使用,建议使用number类型

总结

有人可能会说,既然oracle提供了这些函数,直接用就好了么,干嘛去分析原理?
首先,如果要让oracle数据库里的对象能实现真正的无缝迁移至其他数据库,肯定需要在其他数据库内对oracle里的逻辑进行模拟,此时必须清楚oracle里的逻辑是怎样的;
其次,了解这些原理后,能对以后开发时应该选择哪种数据类型或者使用哪个函数提供指导依据,比如哪种方式计算快或者怎么存储更节省空间。比如这个问答中的回复就使用到了相关知识 oracle里面number(20,2)数据类型,数据精度是20,小数位数是2。那它的数据长度是多少?

  • 本文作者: DarkAthena
  • 本文链接: https://www.darkathena.top/archives/about-utl-raw-and-emulate-cal
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!

【ORACLE】详解oracle数据库UTL_RAW包各个函数的模拟算法相关推荐

  1. 【C语言详解】——常见字符和字符串函数及其模拟实现

    本文主要介绍一些常见的字符和字符串函数及其模拟实现 所需要的头文件 #include<string.h> 目录 1.求字符串长度 strlen 1.1模拟实现 strlen(三种方法) 2 ...

  2. oracle数据库按日期查询,关于Oracle数据库日期范围查询的两种实现方法详解,oracle详解...

    关于Oracle数据库日期范围查询的两种实现方法详解,oracle详解 Oracle数据库日期范围查询有两种方式:to_char方式和to_date方式,接下来我们通过一个实例来介绍这一过程.我们假设 ...

  3. [强烈推荐]ORACLE PL/SQL编程详解之七:程序包的创建与应用(聪明在于学习,天才在于积累!)...

    [强烈推荐]ORACLE PL/SQL编程详解之七: 程序包的创建与应用(聪明在于学习,天才在于积累!) --通过知识共享树立个人品牌.   继上七篇:            [推荐]ORACLE P ...

  4. 视频教程-Oracle数据库从入门到实用教程详解-Oracle

    Oracle数据库从入门到实用教程详解 全栈工程师,2010年从事软件开发以及软件教育培训工作,至今将近十余年,在项目的开发,设计,到管理上积累了丰富的实战经验,教学风格上通俗易懂,问题解答环节一对一 ...

  5. Oracle一张表写多个触发器,详解oracle中通过触发器记录每个语句影响总行数

    详解oracle中通过触发器记录每个语句影响总行数 需求产生: 业务系统中,有一步"抽数"流程,就是把一些数据从其它服务器同步到本库的目标表.这个过程有可能 多人同时抽数,互相影响 ...

  6. 详解Oracle架构、原理、进程,学会世间再无复杂架构

    详解Oracle架构.原理.进程,学会世间再无复杂架构 学习是一个循序渐进的过程,从面到点.从宏观到微观,逐步渗透,各个击破,对于Oracle, 怎么样从宏观上来理解呢?先来看一个图,这个图取自于教材 ...

  7. 详解Oracle AWR运行日志分析工具

    在Oracle数据库学习和使用中,遇到性能问题,首要的步骤就是导出AWR分析报告,AWR是Oracle的一个脚本工具,通过周期性快照记录下当时的所有运行数据,数据库管理员可以导出其中一部分数据进行分析 ...

  8. 深入详解Oracle data change notification

    深入详解 Oracle  data change notification   0.什么是 Oracle  data change notification  ? 当有多个应用程序或者进程操作同一个数 ...

  9. oracle中调试存储过程,详解Oracle调试存储过程

    详解Oracle调试存储过程 一 调试关键步骤 1.在要调试的存储过程右键,选择编辑以进行调试,截图如下: 2.点击小瓢虫,弹出调试窗口,截图如下: 3.输入7839员工编号,点击确认,进行调试,截图 ...

最新文章

  1. 2021《程序员》数字科技企业研发实力榜TOP50
  2. 个人站长状告Google Ads霸王条款
  3. 2018.4.13 用java配置/生成Xml文件 结合IO流知识点
  4. 北京、广东重名数量查询工具,给孩子起名重名查询
  5. (转)怎么实时查看mysql当前连接数
  6. 论MOS管开关对电源的影响
  7. 公式中表达单个双引号【】和空值【】的方法及说明
  8. 怎么在mysql查询自己建的表格_oracle数据库中怎么查询自己建的表
  9. JAVA——以ReentrantLock为例学习重入锁以及公平性问题
  10. 【BlackHat】速修复!有人正在扫描 Exchange 服务器寻找 ProxyShell 漏洞
  11. 带aidl文件的应用程序在android平台源码中的编译
  12. PyCharm错误解决办法:ModuleNotFoundError: No module named 'matplotlib'
  13. excel联系导入到手机通讯录(小米手机)
  14. 包装设计实战案例教学
  15. 对WEB安全工程师的理解
  16. 软件测试工程师面试题及答案
  17. Vue音乐--排行榜页面02_抓取首页数据
  18. FBX文件导入Unity导致贴图丢失问题解决,以3ds max,Blender导入导出为例
  19. 零基础Ar学习之Unity3D运行EasyAr Sample
  20. python奇数偶数机器语言_python 学习笔记之基础1

热门文章

  1. [工具]更新音乐下载网站,MP3音乐无损音乐下载器
  2. 数据挖掘利器 selenium实战案例--论文数据挖掘与可视化分析(下)
  3. keras学习-循环层Recurrent-包装器Wrapper-自己的
  4. mysql无法在kvm虚拟机上_使用KVM虚拟机遇到的问题(持续更新)
  5. 没有哈密瓜只有哈密顿----图论之哈密顿回路
  6. 最好听的钢琴曲排行榜 世界上最好听的钢琴曲
  7. NS2中的数据包common头结构hdr_cmn
  8. php程序员的自白,程序员考试前的内心独白
  9. csdn博客 代码块的显示设置以及图片的插入技巧
  10. 3、u-boot-2016 - board_init_f