我有许多信息源,这些信息需要存储在表中(以及其他信息)。到目前为止,我不知道会有哪些来源。业务逻辑并不需要源,而是仅出于调查目的而存储源。另外,此表仅在生产中用于数据迁移一次,因此我想使解决方案尽可能简单(即,不做适当规范化的表结构)。
我可以为每个源创建一个布尔列(如source1 char(1) default '0', source2 char(1) default '0'
等。)但是,我必须为每个新源添加一个列。我想拥有的是一列位数组,每一位代表一个来源。这与documentation for the BITAND function中提到的order_status列非常相似。
我的问题是,
我也欢迎其他想法,只要添加新的源代码不需要更改表结构。 (我知道在数据库表中使用位数组很少是一个好主意,但这是特殊情况,因此无需对此发表评论。)我们的数据库为12c(12.1)。
答案 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
个) )行。