4

是否有一种类型或方法如何以二进制级别在 oracle 中存储数据。我对 dml at table 和 pl/sql 的操作都感兴趣。

目前所有二进制元素都存储为 varchar2(1000)='11111...0000.1111' 但是操作和数据存储量相当大,因此需要一些优化解决方案。如果此数据可以以二进制格式存储,则需要 1000/8 字节(具有 >7 亿条记录)

也许解决方案是对这些操作使用某种 java+oracle 组合。

欢迎提出想法和建议。

4

5 回答 5

8

如果要存储最多 4000 字节的二进制数据,请使用RAW数据类型。数据将存储为一串字节,无需字符集转换。

使用UTL_RAW包对RAWs.

LONG RAW 数据类型已弃用,当您需要处理超过 4000 字节的数据时,应切换到BLOB 。

于 2012-06-25T13:47:44.517 回答
7

请参阅 Vincent Malgrat 的回答:如果您想在 Oracle 中存储和处理二进制数据,那么RAW数据类型就是要走的路。

(正如 alegen 的回答所暗示的,如果您的意图是存储和检索不需要在数据库中“处理”的图像、视频、音频或压缩数据,但您只是“存储”它并“检索”它,那么BLOB数据类型可能更合适;

(注意:RAW 数据类型限制为 4000 字节,BLOB 数据类型不是。出于性能原因,我更喜欢将 RAW 用于更短的值(例如,200 字节或更少),我需要定期访问值。对于更长的值,很多查询没有引用二进制数据,我倾向于支持BLOB。(这完全是由于内部存储方式的差异RAWBLOB内联存储与单独的块,拆分行,适合块的行数等)

对于您描述的特定问题,根据您提供的信息,RAW听起来像是要走的路。您指定您有 1000 位的序列,但完全不清楚这是一个常数,还是一个最大长度,或者您是否已将较长的二进制数据字符串分解为更易于管理的块以适合列。(如果你真的在处理一个单一的、巨大的二进制数据块,你真的希望避免将它“切碎”成一堆小块,并将每一块存储在单独的行中。这样会更有效率将它们全部存储为一个 BLOB,并将其作为一个简单的流使用。

所有这些都会真正影响您决定是使用 BLOB 还是 RAW。


除此之外,关于从 VARCHAR2 表示的 1 和 0(例如,'00101010',将真实信息的每个“位”存储为单独的字符)转换为更有效地存储的二进制表示的问题,每个“8 位” “需要一个字节存储的真实信息......

OracleRAW数据类型将使您将 8 位存储到单个字节中。也就是说,RAW(125) 将存储您的 VARCHAR2(1000) 的等价物,这将为您每行节省 875 个字节(对于 SBCS,如果您使用的是 DBCS,则是两倍多)。这将显着降低存储需求,让您在一个块中获得更多行,并允许获得更好的性能。

要将当前存储为VARCHAR2一串一和零的数据转换,我不知道有任何内置函数可以做到这一点。但是滚动您自己的函数将二进制字符串表示形式转换为十六进制字符串表示形式相当简单。之后,您可以使用内置HEXTORAW函数转换为RAW.

这是一个可以用作起点的示例。

(注意:此函数只是一个示例,当输入字符串的长度不是 8 个字符的倍数时,它不能有效处理情况。此外,它的行为是字符串值包含除 '1' 或 '0' 以外的字符可能不合适(正如它所写的那样,它将“0”以外的任何字符视为“1”。但是,作为起点已经足够了)。

create or replace function binstr_to_hexstr
( as_binstr in varchar2 ) return varchar2
is
  li_n binary_integer default 0;
  ls_hexstr varchar2(16) default '0123456789ABCDEF';
  ls_return varchar2(2000) default '';
begin
  if ( as_binstr is null ) then
    return null;
  end if;
  ls_return := '';
  li_n := 0;
  for i in 1 .. length(as_binstr) loop
    li_n := li_n*2 + abs(instr('01',substr(as_binstr,i,1))-1);
    if mod(i,4) = 0 then
      ls_return := ls_return || substr(ls_hexstr,li_n+1,1);
      li_n := 0;
    end if;
  end loop;
return ls_return;
end;
/

SELECT binstr_to_hexstr('00101010') AS hexstr FROM DUAL UNION ALL
SELECT binstr_to_hexstr('00x0 010') FROM DUAL;

HEXSTR                                                                             
------
2A
2A

注意:仅当输入字符串的长度是 8 的偶数倍(即 )时,此函数才会返回预期结果(匹配的十六进制表示MOD(length(as_binstr),8) = 0)。否则,函数“丢失”尾随位和/或返回奇数个十六进制数字。(当输入参数的长度不是 8 的倍数时,可以修改该函数以引发异常。)

HEXTORAW和函数在使用客户端应用程序(例如 TOAD、SQL Developer 或 SQL*Plus)RAWTOHEX处理数据时非常有用。RAW(该HEXTORAW函数用于将binstr_to_hexstr函数的输出转换为 RAW。)例如:

create or replace function binstr_to_raw
( as_binstr in varchar2 ) return raw
is
begin
  return hextoraw(binstr_to_hexstr(as_binstr));
end;
/

正如文森特马尔格拉特在他的回答中指出的那样,Oracle 提供了几个包(例如UTL_RAWUTL_ENCODE),它们在处理 RAW 数据时很有用。

http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/u_raw.htm

于 2012-06-28T15:42:38.970 回答
2

为此,您可以使用BLOB(Binary Large OBject) 类型。链接到如何使用它的示例。

于 2012-06-25T11:19:56.567 回答
2

截至今天,我在 10 和 11g 上使用过这个,我认为答案需要更新。这类数据的内置数据类型是(比PL/SQLRAW更容易操作的二进制数据)。BLOB

顾名思义,RAW数据类型是原始二进制格式并按原样存储(第一个块的长度字节除外),因此是该行的最佳存储空间。RAW可以使用SYS.UTL_RAW包来操作数据类型。它可以转换为几乎任何数据类型(如果存储方案匹配。例如b1101不是NUMBER13,而是PL_INTEGER13VARCHAR2CHR(13).

UTL_RAW此外,与使用 进行转换相比,HEXTORAWand函数更容易将数据从十六进制表示的数据RAWTOHEX来回转换为. 例如,或两者都导致值; 如您所见,额外的零被填充到左侧以形成一个字节,因为 a 的基本块是一个字节。在转换到和其他流期间,数据本身充当小端(意味着最低有效字节驻留在数据的最右边字节上),但在转换为复杂数据类型时,它只会复制内存,并填充零到如果需要的话,左边。RAWVARCHAR2HEXTORAW('d')HEXTORAW('D')RAWb00001101RAWVARCHAR2NUMBER

于 2015-04-20T17:39:13.503 回答
-1

我不确定这是否有帮助,但您可以使用 NUMBER 数据类型和bin_to_num函数:

create table test_bin(num_val number, var_val varchar2(1000));

insert into test_bin values(bin_to_num(1,0,1,0,1,0,1,0), '10101010');

select dump(num_val, 17) n, dump(var_val, 17) v from test_bin;

n                     |   v
---------------------------------------------------
Typ=2 Len=3 c2,^B,G   |   Typ=1 Len=8 1,0,1,0,1,0,1,0

如您所见,它是排序器 - 而不是 8 个字节,您只有 3 个字节,但它仍然不是一个字节

更新 找到了一种将它放在一个字节中的方法:您可以使用该chr函数

select dump(chr(bin_to_num(1,0,1,0,1,0,1,0))) c from dual;

n                   
--------------------
Typ=1 Len=1 170

因此,您可以继续使用 varchar2 但只需将每 8 位转换为 char

于 2012-06-25T13:59:28.863 回答