Битовые операции
Битовые операции при работе с БД применяются редко. Тем не менее, работа с отдельными битами поддерживается в БД Oracle, и в некоторых случаях может быть использована в весьма элегантном виде.
Тестовые данные
create table docs(
id number not null primary key,
doc_num varchar2(100 char) not null,
bit_access number default 0 not null
);
comment on table docs is 'Документы';
comment on column docs.bit_access is 'Уровни доступа(1 бит - чтение, 2 - редактирование, 3 - удаление)';
insert into docs
values(1, '1-1', 0);
insert into docs
values(2, '2-1', 1);
insert into docs
values(3, '2-2', 4);
insert into docs
values(4, '3-1', 3);
insert into docs
values(5, '4-1', 7);
create table docs(
id number not null primary key,
doc_num varchar2(100 char) not null,
bit_access number default 0 not null
);
comment on table docs is 'Документы';
comment on column docs.bit_access is 'Уровни доступа(1 бит - чтение, 2 - редактирование, 3 - удаление)';
insert into docs
values(1, '1-1', 0);
insert into docs
values(2, '2-1', 1);
insert into docs
values(3, '2-2', 4);
insert into docs
values(4, '3-1', 3);
insert into docs
values(5, '4-1', 7);
Колонка bit_access
хранит в себе число, каждый бит которого отвечает за наличие(1) или отсутствие(0) доступа на произведение операций с данной строкой таблицы(документом). То есть, если в числе, находящемся в колонке bit_access
, первый бит равен 1, это означает, что данную запись можно показывать пользователю. Если второй бит равен 1, то данную строку можно редактировать, а если третий бит установлен в 1, то данную строку можно удалять. Если мы представим наше число в виде трех бит, оно будет иметь вид 000
. Порядок бит в записи числа как правило идет справа налево, т.е. если в числе установлен первый бит в 1, то оно записывается как 001
, если второй, то 010
.
При этом доступ может быть комбинированным - мы можем иметь доступ и на просмотр информации по документу, и на удаление самого документа, или, скажем, на просмотр и редактирование, но не на удаление. В таких случаях в нашем числе несколько бит числа будут установлены в 1(Само число в двоичной записи будет 101
в первом случае и 011
во втором).
Подобным образом можно закодировать несколько логических переменных в одно число, в котором каждый бит будет отвечать за соответствующее условие.
Для того, чтобы проверить, установлен ли определенный бит числа в 1, применяется операция, которая называется побитовой конъюнкцией, или побитовым "И". Результатом побитового "И" между числами a
и b
будет такое число c
, у которого в 1 будут установлены только те биты, которые установлены в 1 как в a
, так и в b
. Вот как это будет выглядеть, если a= 011
, а b=110
:
A | 0 | 1 | 1 |
---|---|---|---|
B | 1 | 1 | 0 |
C | 0 | 1 | 0 |
Таким образом, чтобы убедиться, что интересующий нас набор бит (предположим, этот набор бит хранится в числе b
) установлен в числе a
, нужно применить побитовое "И" к числам a
и b
, и получившийся результат сравнить с числом b
(еще это число называют битовой маской) - если результат совпал, значит, все биты, установленные в числе b
установлены и в числе a
.
В таблице числа мы храним в десятичной системе, и чтобы было проще ориентироваться, распишем, что означают текущие данные в таблице:
bit_access | двоичное представление | Доступ |
---|---|---|
0 | 000 | Ничего нельзя делать |
1 | 001 | Чтение |
3 | 011 | Чтение и редактирование |
7 | 111 | Чтение, редактирование, удаление |
BIN_TO_NUM
Эта функция принимает список нулей и единиц, превращая их в десятичное число:
select bin_to_num(0,0,0) a,
bin_to_num(0,0,1) b,
bin_to_num(0,1,1) c,
bin_to_num(1,1,1) d
from dual
select bin_to_num(0,0,0) a,
bin_to_num(0,0,1) b,
bin_to_num(0,1,1) c,
bin_to_num(1,1,1) d
from dual
A | B | C | D |
---|---|---|---|
0 | 1 | 3 | 7 |
BITAND. Побитовое "И"
Функция BITAND
выполняет побитовое "И" между двумя числами.
Выведем список всех документов, которые доступны для чтения:
select *
from docs
where bitand(bit_access, 1) = 1
select *
from docs
where bitand(bit_access, 1) = 1
ID | DOC_NUM | BIT_ACCESS |
---|---|---|
2 | 2-1 | 1 |
4 | 3-1 | 3 |
5 | 4-1 | 7 |
Список всех документов, которые доступны для чтения и редактирования:
select *
from docs
where bitand(bit_access, 3) = 3
select *
from docs
where bitand(bit_access, 3) = 3
ID | DOC_NUM | BIT_ACCESS |
---|---|---|
4 | 3-1 | 3 |
5 | 4-1 | 7 |
Чтобы было более наглядно и читаемо, последний запрос можно переписать с использованием фукнции bin_to_num
:
select *
from docs
where bitand(bit_access, bin_to_num(0,1,1)) = bin_to_num(0,0,1)
select *
from docs
where bitand(bit_access, bin_to_num(0,1,1)) = bin_to_num(0,0,1)
Для улучшения читаемости кода лучше использовать bin_to_num
для записи битовых масок.
Выведем список всех документов, и добавим к выборке три колонки, каждая из которых будет отвечать за наличие доступа к определенному действию:
select id,
doc_num,
bit_access,
case
when bitand(bit_access,
bin_to_num(0,0,1)) = bin_to_num(0,0,1) then 'Да'
else 'Нет'
end read_access,
case
when bitand(bit_access,
bin_to_num(0,1,0)) = bin_to_num(0,1,0) then 'Да'
else 'Нет'
end edit_access,
case
when bitand(bit_access,
bin_to_num(1,0,0)) = bin_to_num(1,0,0) then 'Да'
else 'Нет'
end delete_access
from docs
select id,
doc_num,
bit_access,
case
when bitand(bit_access,
bin_to_num(0,0,1)) = bin_to_num(0,0,1) then 'Да'
else 'Нет'
end read_access,
case
when bitand(bit_access,
bin_to_num(0,1,0)) = bin_to_num(0,1,0) then 'Да'
else 'Нет'
end edit_access,
case
when bitand(bit_access,
bin_to_num(1,0,0)) = bin_to_num(1,0,0) then 'Да'
else 'Нет'
end delete_access
from docs
ID | DOC_NUM | BIT_ACCESS | READ_ACCESS | EDIT_ACCESS | DELETE_ACCESS |
---|---|---|---|---|---|
1 | 1-1 | 0 | Нет | Нет | Нет |
2 | 2-1 | 1 | Да | Нет | Нет |
3 | 2-2 | 4 | Нет | Нет | Да |
4 | 3-1 | 3 | Да | Да | Нет |
5 | 4-1 | 7 | Да | Да | Да |