Oracle列中的位数组

时间:2019-01-30 11:13:55

标签: oracle bitarray

我有许多信息源,这些信息需要存储在表中(以及其他信息)。到目前为止,我不知道会有哪些来源。业务逻辑并不需要源,而是仅出于调查目的而存储源。另外,此表仅在生产中用于数据迁移一次,因此我想使解决方案尽可能简单(即,不做适当规范化的表结构)。

我可以为每个源创建一个布尔列(如source1 char(1) default '0', source2 char(1) default '0'等。)但是,我必须为每个新源添加一个列。我想拥有的是一列位数组,每一位代表一个来源。这与documentation for the BITAND function中提到的order_status列非常相似。

我的问题是,

  1. 假设最大列数是此列的首选数据类型是什么。 16个来源? NUMBER(2)?
  2. 我将如何更新此字段(例如,将位号设置为3)?我一直在研究UTL_RAW函数,但它们似乎都(惊讶,惊讶)期望RAW输入,这使事情变得有些麻烦。

我也欢迎其他想法,只要添加新的源代码不需要更改表结构。 (我知道在数据库表中使用位数组很少是一个好主意,但这是特殊情况,因此无需对此发表评论。)我们的数据库为12c(12.1)。

2 个答案:

答案 0 :(得分:3)

创建对象类型:

Oracle设置

CREATE TYPE bitarray AS OBJECT(
  data   BLOB,
  len    NUMBER(38,0),
  CONSTRUCTOR FUNCTION bitarray( in_length NUMBER ) RETURN SELF AS RESULT,
  CONSTRUCTOR FUNCTION bitarray( in_data VARCHAR2 ) RETURN SELF AS RESULT,
  MEMBER FUNCTION getBit( in_index NUMBER ) RETURN NUMBER,
  MEMBER FUNCTION setBit( in_index NUMBER, in_value NUMBER ) RETURN bitarray,
  MEMBER FUNCTION toString RETURN CLOB,
  STATIC FUNCTION byteToRaw( in_value BINARY_INTEGER ) RETURN RAW
);
/

CREATE TYPE BODY bitarray AS
  CONSTRUCTOR FUNCTION bitarray( in_length NUMBER ) RETURN SELF AS RESULT
  AS
    p_raw RAW(1) := BITARRAY.BYTETORAW( 0 );
  BEGIN
    DBMS_LOB.CREATETEMPORARY( SELF.DATA, FALSE );
    SELF.LEN  := in_length;
    FOR i IN 1 .. CEIL( in_length / 8 ) LOOP
      DBMS_LOB.WRITEAPPEND( SELF.DATA, 1, p_raw );
    END LOOP;
    RETURN;
  END;

  CONSTRUCTOR FUNCTION bitarray( in_data VARCHAR2 ) RETURN SELF AS RESULT
  AS
    p_value  BINARY_INTEGER := 0;
    p_power  BINARY_INTEGER := 1;
  BEGIN
    SELF.LEN  := LENGTH( in_data );
    DBMS_LOB.CREATETEMPORARY( SELF.DATA, FALSE );
    FOR i IN 1 .. SELF.LEN LOOP
      IF SUBSTR( in_data, i, 1 ) = '1' THEN
        p_value := p_value + p_power;
      END IF;
      IF MOD( i, 8 ) = 0 OR i = SELF.LEN THEN
        DBMS_LOB.WRITEAPPEND( SELF.DATA, 1, BITARRAY.BYTETORAW( p_value ) );
        p_value := 0;
        p_power := 1;
      ELSE
        p_power := p_power * 2;
      END IF;
    END LOOP;
    RETURN;
  END;

  MEMBER FUNCTION getBit( in_index NUMBER ) RETURN NUMBER
  AS
    p_amount     BINARY_INTEGER := 1;
    p_raw        RAW(1);
    p_bit_index  BINARY_INTEGER := MOD( in_index - 1, 8 );
    p_byte_index BINARY_INTEGER := ( in_index - 1 - p_bit_index ) / 8 + 1;
    p_bit_value  BINARY_INTEGER := POWER( 2, p_bit_index );
  BEGIN
    IF in_index IS NULL OR in_index < 1 OR in_index > SELF.LEN THEN
      RETURN NULL;
    END IF;
    DBMS_LOB.READ( SELF.DATA, p_amount, p_byte_index, p_raw );
    RETURN BITAND( UTL_RAW.CAST_TO_BINARY_INTEGER( p_raw ), p_bit_value ) / p_bit_value;
  END;

  MEMBER FUNCTION setBit( in_index NUMBER, in_value NUMBER ) RETURN bitarray
  AS
    p_amount     BINARY_INTEGER := 1;
    p_raw        RAW(1);
    p_bit_index  BINARY_INTEGER := MOD( in_index - 1, 8 );
    p_byte_index BINARY_INTEGER := ( in_index - 1 - p_bit_index ) / 8 + 1;
    p_bit_value  RAW(1)         := BITARRAY.BYTETORAW( POWER( 2, p_bit_index ) );
    p_array      bitarray       := SELF;
  BEGIN
    IF in_index IS NULL OR in_value NOT IN ( 0, 1 ) OR in_index < 1 OR in_index > SELF.LEN THEN
      RETURN p_array;
    END IF;
    DBMS_LOB.READ( SELF.DATA, p_amount, p_byte_index, p_raw );
    IF in_value = 1 THEN
      p_raw := UTL_RAW.BIT_OR( p_raw, p_bit_value );
    ELSE
      p_raw := UTL_RAW.BIT_AND( p_raw, UTL_RAW.BIT_COMPLEMENT( p_bit_value ) );
    END IF;
    DBMS_LOB.WRITE( p_array.DATA, p_amount, p_byte_index, p_raw );
    RETURN p_array;
  END;

  MEMBER FUNCTION toString RETURN CLOB
  AS
    p_string CLOB := EMPTY_CLOB();
  BEGIN
    FOR i IN 1 .. SELF.LEN LOOP
      IF SELF.getBit(i) = 0 THEN
        p_string := p_string || '0';
      ELSIF SELF.getBit(i) = 1 THEN
        p_string := p_string || '1';
      ELSE
        p_string := p_string || '-';
      END IF;
    END LOOP;
    RETURN p_string;
  END;

  STATIC FUNCTION byteToRaw( in_value BINARY_INTEGER ) RETURN RAW
  AS
  BEGIN
    RETURN UTL_RAW.SUBSTR( UTL_RAW.CAST_FROM_BINARY_INTEGER( in_value ), 4, 1 );
  END;
END;
/

查询

然后您可以在SQL中使用它:

SELECT BITARRAY(5).toString() AS default_value,
       BITARRAY('10110').toString() AS with_values,
       BITARRAY('10110').setBit(3,0).toString() AS set_values
FROM   DUAL;

输出

DEFAULT_VALUE | WITH_VALUES | SET_VALUES
:------------ | :---------- | :---------
00000         | 10110       | 10010     

存储在表中

CREATE TABLE table_name ( id INT, bits BITARRAY );
INSERT INTO table_name
SELECT 1, bitarray( 4 ).setBit( 1, 1 ).setBit( 4, 1 ) FROM DUAL UNION ALL
SELECT 1, bitarray( '1011001' ) FROM DUAL;

然后使用以下查询它:

SELECT id, t.bits.toString() FROM table_name t;

输出:

ID | T.BITS.TOSTRING()
-: | :----------------
 1 | 1001             
 1 | 1011001          

db <>提琴here

答案 1 :(得分:1)

代替创建这样的,如何创建?它包含两列:

  • 源名称
  • 布尔信息

例如:

SQL> create table that_table
  2    (source_name varchar2(30),
  3     cb_bool     number(1) default 0 not null
  4    );

Table created.

SQL> insert into that_table
  2    select 'source 1', 0 from dual union all
  3    select 'source 2', 1 from dual union all
  4    select 'source 9', 1 from dual;

3 rows created.

SQL> select * From that_table;

SOURCE_NAME                       CB_BOOL
------------------------------ ----------
source 1                                0
source 2                                1
source 9                                1

SQL>

与您的想法相反,它可以扩展,并且实际上有多少资源并不重要-您只需INSERT个新资源(或现有UPDATE个) )行。