Создание типа объекта:
Установка 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 <> fiddle здесь