【ORACLE】详解oracle数据库UTL_RAW包各个函数的模拟算法
前言
這篇文章可能是你至今(2022-02-11)能在互聯網看到的,關于utl_raw包的邏輯說得最深入的一篇文章了。
由于最近在復刻oracle中自帶的包到其他數據庫,因此需要對oracle中的包的邏輯進行解析。
比如UTL_RAW這個包,以前用得挺多,但沒深究其函數邏輯,這次仔細分析,發現了有一些函數涉及到了一些計算機基本原理及IEEE標準,比較有意思,因此寫一篇這樣的文章來分享。
函數清單
先上官方文檔 UTL_RAW
再貼個函數清單,稍微翻譯了下,方便理解
| 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
作用:
計算位與
例:
算法:
十六進制 F0 轉二進制 11110000
十六進制 BA 轉二進制 10111010
兩個數字按位,逐個進行匹配,相等輸出1,不等輸出0
得10110000,然后轉十六進制,得B0
2.BIT_OR
作用:
計算位或
例:
算法:
十六進制 F0 轉二進制 11110000
十六進制 BA 轉二進制 10111010
兩個數字按位,逐個進行匹配,至少有個1則輸出1,都是0則輸出0
得11111010,然后轉十六進制,得FA
3.BIT_XOR
作用:
計算位異或
例:
算法:
十六進制 F0 轉二進制 11110000
十六進制 BA 轉二進制 10111010
兩個數字按位,逐個進行匹配,相等輸出0,不等輸出1
得01001010,然后轉十六進制,得4A
4.BIT_COMPLEMENT
作用:
計算補碼
例:
算法:
EA轉二進制 11101010
用二進制 11111111減去它(或者理解為1變0,0變1),
得二進制 00010101,再轉十六進制,得15
5.CONCAT
作用:
拼接多個raw,最多拼12個
例:
算法:
EAAB 轉二進制 1110101010101011
3A 轉二進制 00111010
計算第二個參數,字節長度為1,占8位,因此第一個參數的二進制向左移動8位,
得111010101010101100000000,
然后加上第二個參數的2進制,得 111010101010101100111010,轉十六進制得 EAAB3A
(可以看到,此例如果是把十六進制數值當成字符串來進行拼接,結果是一樣的,但要注意是按字節長度拼接,比如 “FF"拼"F”,得到應該是 “FF0F”,而不是字符串拼接的"FFF")
6.LENGTH
作用:
計算長度,單位:字節
例:
算法:
EAEA轉二進制 1110101011101010
從后往前數,8位為1個字節,不足8位的前面補0
(可以將十六進制數值視同為字符串,該字符串長度的一半即為原數據的字節長度,但同樣要注意省去了前面的0的情況,比如"FFF"按字符串的長度是1.5,明顯不對,應該計算"0FFF"長度的一半)
7.SUBSTR
作用:
按字節截取
例:
算法:
(轉二進制過程略)
AB 為一個字節, CD為一個字節,從第3字節 EF 開始,截取 2個字節長度,即 EF 12
(和字符串的substr類似,可以理解為2個字符一組,第3個參數為空時表示一直截到最后;第2個參數同樣支持負數,即從倒數第n位開始截;但是第3個參數要截的長度不能超過raw的剩余長度,否則會報錯ora-06502)
8.COPIES
作用:
復制自己成多份
例:
算法:
(轉二進制過程略)
1A1B字節長度為2,往左移兩個字節長度,得1A1B0000
再加上自己,得1A1B1A1B,繼續左移兩個字節長度,得1A1B1A1B0000
再加上自己,得1A1B1A1B1A1B
可以發現左移次數為第2個參數減1次
(也可以簡單的視為字符串拼接,但同樣要注意前面補0,比如utl_raw.COPIES(‘FFF’,2) 的結果應該是0FFF0FFF)
9.REVERSE
作用:
按字節進行翻轉
例:
算法:
(轉二進制過程略)
01020304字節長度為4,
取第4個字節04,左移3個字節,得04000000
取第3個字節03,左移2個字節,得00030000
取第2個字節02,左移1個字節,得00000200
取第1個字節01,得00000001
把上面4個數加起來,得04030201
(也可以簡單的視為字符串,只不過是2個字符為一組進行翻轉,不要忘了補0)
10.XRANGE
作用:
根據傳入的兩個參數作為開始數值和結束數值,以1遞增,生成一串序列,注意兩個參數均只接受第一個字節,后面的字節會被忽略
例:
算法:
(轉二進制過程略)
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
作用:
按字節進行替換,(由于第二個參數和第三個參數的長度允許不一致,因此以填充碼的方式,確保原數據和輸出結果的長度一致)
例:
算法:
(轉二進制過程略)
第一個參數是原數據,第二個參數是要替換成的數據,第三個參數是要查找的數據,第4個參數是填充碼
此例中,對第一個參數進行以下字節的覆蓋
01 -> 08
02 -> 09
03 -> 空(取填充碼0A)
04 -> 空(取填充碼0A)
得輸出結果 08 09 0A 0A 05 09
12.TRANSLATE
作用:
按字節進行替換(無填充碼參數,因此輸入和輸出的長度可能不一致)
例:
算法:
和TRANSLITERATE函數類似,不過沒有填充碼,沒對應上的則替換成空(二進制數據需要右移),而且注意第二個參數和第三個參數反過來了
13.OVERLAY
作用:
按指定開始字節及長度覆寫字節數據
用法:
第一個參數為覆寫碼,第二個參數為原始數據,第3個參數為開始字節位置(默認值為1,表示從第一個字節開始),第4個參數為覆蓋字節長度(默認值為剩余的所有字節長度),第5個參數為填充碼(默認值為00)
算法:(以最后一個為例)
(轉二進制過程略)
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),返回一個整數
算法:
(轉二進制過程略)
第一例,由于前兩個參數長度不一致,且第一個參數長度較短,因此使用第三個參數對其進行填充至相同長度,得 01020304,所以兩個完全一致,返回0
第二例,可以發現第二個字節 05 不等于 02,因此返回2
15.CAST_TO_RAW
作用:
將varchar2類型的數據的二進制數據轉換成raw類型
例:
算法:
略,
注意第二例,傳入的參數是字符,不是數字,
第三例中,對于非單字節字符,強制指定字符集進行寫入,否則寫入的raw不可控
16.CAST_TO_VARCHAR2
作用:
將raw格式的字符串使用數據庫默認字符集轉換回VARCHAR2(其實就是CAST_TO_RAW函數的逆向函數)
例:
算法:
略,
注意,由于此函數不能指定字符集,因此轉換回來可能會亂碼,可以使用utl_i18n.raw_to_char這個函數進行替代
17.CONVERT
作用:
轉換RAW的字符集
例:
算法:略
可用下列方式驗證
18.CAST_FROM_BINARY_INTEGER
作用:
將一個整數的二進制數據轉換成RAW
用法:第1個參數為要進行轉換的整數,第二個參數為大端還是小端,默認大端為1,小端為2,傳3為取機器配置的端
算法:
其實就是把十進制的整數轉換成其二進制數據的十六進制展現形式,但最長只能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為取機器配置的端
算法:
略,注意此處可以傳入小于或者等于4個字節的raw,超過4個字節會報錯
20.CAST_FROM_NUMBER
作用:
獲得一個number類型值存儲的二進制數據
例:
算法:
這里內容會比較多,目前國內很難找到關于這個算法的中文說明,用英文關鍵詞去搜也很難找到,但是我搜到了一個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的逆向函數
例:
算法:
略,見上面的CAST_FROM_NUMBER
22.CAST_TO_NVARCHAR2
作用:
把RAW轉換回NVARCHAR2
例:
算法:
其實NVARCHAR2類型就是用unicode編碼來存儲數據,可以使用unistr或者asciistr函數驗證
但是,要注意的是,對于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包各个函数的模拟算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年第八届大唐杯全国大学生移动通信
- 下一篇: php中实现记住密码下次自动登录的例子