Website development
25 August 2008

Хранение набора чекбоксов в одном поле БД. Битовая маска.

Часто при составлении сайтов с каталогами нам приходится оперировать с однотипными карточками каталога. Рассмотрим пример:
У нас имеется каталог СТО которые оказывают различные услуги клиентам. Есть предопределенный список услуг, среди которых производится выбор. Список услуг представляет собой набор чекбоксов, если услуга оказывается то чекбокс помечается, в противоположном случае отметка не ставится. Предположим количество услуг в каталоге равняется десяти(просто для определенности).
Самым простым способом было бы хранение состояния каждого чекбокса в отдельном поле, однако при этом мы для каждой новой услуги(которая будет указываться на сайте и учавствовать в поиске) будем вынуждены расширять таблицу, и изменять поиск.
Но мы пойдем другим путем.

В ходе обсуждения среди разрабтчиков возникла идея хранить состояние всех чекбоксов в одном поле. Поскольку чекбоксы могут принимать только два состояния(1(флаг поставлен) и 0(флаг не поставлен)), получается простая битовая логика. В итоге мы можем хранить в mysql в поле целого типа 32 чекбокса(при условии что поле может содержать 32-х битные значения). При сохранении значения формы мы просто берем значение чекбокса(если оно не нулевое)смещаем его на номер чекбокса влево (нумерация чекбоксов начинается с нуля(как и нумерация бит)), полученное значение соедияем с полученным на предыдущем шаге с помощью побитовой операции OR. Пример кода на php:
<?
//Количество полей
$n=10;
//Результирующее значение
$resultValue=0;
for($i=0;$i<$n;++$i){
  if(!empty($formValue[$i])){
    $resultValue|=$formValue[$i]<<$i;
  }
}
?>


При извлечении данных мы выполняем обратное преобразование для получения исходных данных.
Однако простого хранения и извлечения данных нам недостаточно, очень бы хотелось сделать еще и поиск. При этом поиск может быть строгим и не строгим. В первом случае надо выбрать все карточки каталога, в которых есть все отмеченные позиции, в втором случае только те карточки, в которых есть хотя бы одна отмеченная позиция.
Mysql позволяет выполнять побитовые операции прямо в запросе. В результате мы получим следующий запрос, где n это число, которое получается в результате упаковки значений чекбоксов, которое мы с помощью побитовой операции AND накладываем на поле с данными(предполагается что упакованные данные хранятся в поле services):
SELECT * FROM `dataTable` WHERE `services` & n = n

Если поиск строгий, то в итоге нам нужны записи, у которых после выполнения побитовых операций над полем services результат будет равен нашему числу. Если поиск нестрогий, то запрос будет таким:
SELECT * FROM `dataTable` WHERE `services` & n > 0

В этому случае нам нужны записи, у которых результат данной операции будет больше нуля.
Хотелось бы отметить что в данном виде можно хранить не только набор чекбоксов.
P.S. Данный материал не претендует на истину в последней инстанции и является концептуальным(хотя уже использовался на нескольких проектах)
UPD. По просьбе пользователя Roxis изменено название

+30
14.3k 91
Comments 64