【问题标题】:oracle binary data typesoracle 二进制数据类型
【发布时间】:2012-06-25 11:15:27
【问题描述】:

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

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

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

欢迎提出想法和建议。

【问题讨论】:

  • 如果您想要二进制操作(bitand 等),请参阅stackoverflow.com/questions/6125055/oracle-type-casting/…
  • Oracle 数据类型 RAW 是要走的路。我过去曾使用这种数据类型来存储和处理 4 字节(32 位)以及 16 字节(128 位)值。请参阅文森特马尔格拉特的回答,我不会发布重申他所说的话的答案。您可以使用 NUMBER 数据类型玩一些游戏,但它有限制,并且变得更加麻烦。 BLOB 数据类型具有更长的优势,但同样,它更麻烦。 RAW 数据类型确实是要走的路。
  • @spencer7593,现在我才看到你的 cmets。使用HEXTORAW 是最好的解决方案。如果您要添加一个答案,说 OP 应该首先将 '10101010' 字符串转换为十六进制字符串,然后使用 hextoraw 将它们插入原始数据类型 - 我会赞成它。它没有使用有问题的字符转换,但它会使其成为一个字节长度
  • @A.B.Cade,我现在看到我不清楚将 VARCHAR2(8) 字符串表示形式转换为 RAW(1) 二进制表示形式。 (我想我认为 OP 引用存储“以二进制格式存储,这将需要 1000/8 字节的存储空间”。

标签: sql oracle oracle11g


【解决方案1】:

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

使用UTL_RAW 包对RAWs 执行操作。

不推荐使用 LONG RAW 数据类型,当您需要处理超过 4000 字节的数据时,应切换到 BLOB

【讨论】:

  • 我使用 RAW 数据类型来存储和处理 4 字节和 16 字节(32 位和 128 位)二进制值。 RAWTOHEXHEXTORAW 函数可以方便地显示和更新来自交互式客户端(例如 TOAD)的 RAW 值,在该客户端中使用字符串要容易得多。我会注意到大部分检索和维护存储的 RAW 值是使用 PL/SQL 完成的。 RAW 最大的优点是字符集转换不是问题,8 个逻辑位在 1 个字节的存储空间中表示。
【解决方案2】:

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

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

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

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

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


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

Oracle RAW 数据类型会将 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的倍数时抛出异常。)

HEXTORAWRAWTOHEX 函数在使用客户端应用程序(例如 TOAD、SQL Developer 或 SQL*Plus)处理 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;
/

正如 Vincent Malgrat 在他的回答中指出的那样,Oracle 提供了几个包(例如 UTL_RAWUTL_ENCODE),它们对于处理 RAW 数据很有用。

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

【讨论】:

    【解决方案3】:

    为此,您可以使用BLOB(二进制大对象)类型。 Link 举例说明如何使用。

    【讨论】:

    • 嗨,blob 没问题,但是它会占用 8 位并将其存储为 1 字节吗?二元运算呢?
    • @SauliusS:当然 8bit 会产生一个字节。你想要一个字节的不同位数吗?
    • 马,问题在 blob:='10101010' 中我的 db 大小是 1 字节还是 8 字节?因为 blob 的 vsize 不起作用
    【解决方案4】:

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

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

    此外,与使用 UTL_RAW 进行转换相比,HEXTORAWRAWTOHEX 函数更容易将数据从 VARCHAR2 中数据的十六进制表示来回转换为 RAW。例如,HEXTORAW('d')HEXTORAW('D') 都导致RAW 值为b00001101;如您所见,由于RAW 的基本块是一个字节,因此将多余的零填充到左侧以形成一个字节。在转换为VARCHAR2 和其他流期间,数据本身充当little-endian(意味着最低有效字节位于数据的最右侧字节),但在转换为NUMBER 等复杂数据类型时,它只复制内存结束,如果需要,在左边填充零。

    【讨论】:

      【解决方案5】:

      我不确定这是否有帮助,但您可以使用 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

      【讨论】:

      • 感谢 A.B.Cade...这也是我的想法,只是我的解决方案是将 8/16 长度的 bin 数据解码为我自己的字母。
      • 对此还有一个问题:select length('a'||chr(254)||'a') from dual 有对此的解释吗?因为我想转换为 char 的一些字符串是 11111101 或 11111110 (该死的如何添加 br 为 ff 2 空格不起作用)
      • ascii表最多只有127个,所以要用7位,所以至少要压缩7个
      • 我不确定我是否理解您的问题:使用dump 函数- chr(254) 的长度为 1 字节(如预期的那样)。我认为你仍然可以使用 8 位,因为 chr(0) != chr(128) (BTW chr(0) = chr(256)) (用select dump(chr(0)), dump(chr(128)), dump(chr(256)) from dual检查)
      • 我必须说,我不确定您将如何使用这样存储的数据。我也看不到将大量数据转换为它的好方法。压缩数据并以压缩的形式插入不是更好吗?
      猜你喜欢
      • 2014-11-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-18
      • 2022-01-14
      • 2021-06-13
      • 1970-01-01
      • 1970-01-01
      • 2017-06-15
      相关资源
      最近更新 更多