"Упакованное" хранение в MS Access

Источник: Vbnet

Вынашивал я одну мыслю - почему при достаточно большом количестве логических полей в таблице (а их обычно бывает более двух) не организовать упаковку в числовое поле? Ведь что такое числовое поле типа Byte? Это массив из 8 битов, которые выглядит так:

8 7 6 5 4 3 2 1 - вот эти биты и составляют число типа Byte. Обратите внимание - самый младший бит идет под номером 1, а самый старший под номером 8. Вот такой обратный отсчет зародится на заре компьютеростроения, когда даже Биллу Гейтсу казалось, что 640 Кб (Килобайт!!!) ОЗУ хватит на всё и больше просто не потребуется никогда.

А что хранит логическое поле типа Boolean ? Да только логическую информацию, типа, да/нет, включено/выключено, мужчина/женщина ;). Для хранения такой информации достаточно одного бита, который принимает состояния 0 и 1 соответственно. То есть, для хранения 8-ми логических полей достаточно 1 байта, для хранения 16 - двух байтов, 32 логических полей - четырех байтов.

Правда, в справке Access XP указывается, что размер логического поля 1 бит. Хм.... если значение True принимает -1, а значение False - 0, то тогда для хранения информации о данном бите все равно необходим хотя бы один байт. Непонятно, чтение литературы по данному вопросу тоже ничего не прояснило. Буду рад услышать аргументированное мнение по данному вопросу.

Логический

Значения «Да» и «Нет», а также поля, содержащие только одно из двух возможных значений (Да/Нет, True/False или Вкл/Выкл).

1 бит

По моему, тут ошибка в справке заключается в том, что для хранения логической информации действительно необходим лишь один бит, но само-то логическое поле должно занимать, как минимум, байт. Биты же не могут храниться отдельно, а только в составе байта, такова архитектура компьютеров x86.

Мы рассмотрим возможность хранения до 8 логических полей в одном поле типа Byte (0-255, занимает 1 байт, во что охотно верится ;). Вы легко сможете расширить пример и для хранения до 32 логических полей, которые будете должны хранить в поле типа Long (0-2147483647, занимает 4 байта).

В отдельный модуль вставьте три функции: 

Public Function SetBitField(lngValue As Long, lngFieldNumber As Long) As Boolean
' установка бита (флага) в указанной позиции
On Error GoTo ErrorLevel
    lngValue = lngValue Imp 2 ^ (8 - lngFieldNumber)
    lngValue = lngValue Imp 2 ^ (8 - lngFieldNumber)
    SetBitField = True
Exit Function
ErrorLevel:
    MsgBox Err & ": " & Err.Description
    SetBitField = False
End Function

Public Function WipeBitField(lngValue As Long, lngFieldNumber As Long) As Boolean
' сброс бита (флага) в указанной позиции
On Error GoTo ErrorLevel
    lngValue = lngValue And (Not (2 ^ (8 - lngFieldNumber)))
    WipeBitField = True
Exit Function
ErrorLevel:
    MsgBox Err & ": " & Err.Description
    WipeBitField = False
End Function

Public Function CheckBitField(lngValue As Long, lngFieldNumber As Long) As Boolean
' проверка бита (флага) в указанной позиции
On Error GoTo ErrorLevel
    lngValue = lngValue And 2 ^ (8 - lngFieldNumber)
    If lngValue <> 2 ^ (8 - lngFieldNumber) Then
        CheckBitField = False
    Else
        CheckBitField = True
    End If
Exit Function
ErrorLevel:
    MsgBox Err & ": " & Err.Description
    CheckBitField = False
End Function

Затем в модуле формы создадим кнопку и в событие [Нажатие кнопки] вставляем данный код:

Private Sub Кнопка0_Click()
Dim bytValueBoolField As Long
On Error GoTo Error_Кнопка0_Click
    ' 00000000 = схематическое представление битов
    ' 12345678 = номера позиций битов для указания при вызовах функций
    bytValueBoolField = 143 ' присвоим тестовое значение 10001111 = 143

    If Not SetBitField(bytValueBoolField, 2) Then MsgBox "Бит не хочет устанавливаться"
    MsgBox bytValueBoolField
    ' при установке бита во второй позиции будет число 207 = 11001111

    If Not WipeBitField(bytValueBoolField, 1) Then MsgBox "Бит не хочет сбрасываться"
    MsgBox bytValueBoolField
    ' при сбросе бита в первой позиции будет число 79 = 01001111

    MsgBox CheckBitField(bytValueBoolField, 1)
    ' проверка бита в первой позиции вернет значение False = бит сброшен
Exit Sub
    Error_Кнопка0_Click:
    MsgBox Err & ": " & Err.Description
End Sub

Теперь в любой момент можно узнать состояние любого бита - достаточно передать требуемой функции два параметра: самое числовое значение и номер позиции бита. Обратите внимание: несмотря на то, что биты считаются компьютером справа налево, номер позиции передается "нормальным человеческим языком", то есть считая слева направо (для этого в функциях есть необходимое преобразование (8 - lngFieldNumber) ).

Конечно, если логических полей в таблице два-три, то может быть возня не стоит свеч ;). Но уже при количестве полей, приближающихся к 8, надо будет серьезно задуматься о таком вот "упакованном" хранении информации.

Еще одна идея: если в числовом значении типа Long хранится 32 бита, то есть 32 отдельных поля, которые можно рассматривать как массив - в них можно хранить дни любого месяца, например: дежурство сотрудника по дням месяца. В свою очередь, в 8-битном массиве можно хранить дни недели. Проверку, сброс, установку всех битов легко организовать в цикл. При работе с такими массивами битов полезно будет держать в голове способы управления битами с помощью логических операторов And, Eqv, Imp, Not, Or, Xor,  например: инверсия (Not) оптимальна для переключения состояния всех битов (команда - инвертировать выделенное).

Преимущества: индексация для каждого логического поля не нужна, т.к. логических полей не существует - выигрыш в размере базы, вместо 8-32 байтов (логических полей) используется один-четыре - выигрыш в 7 раз. Меньше полей - меньше путаницы и захламления конструктора и таблиц.

Недостатки: необходимо дополнительное вычисление для получения результата, необходимо планирование и документирование диапазона битов (а где оно не нужно?)

Вывод: при грамотном подходе можно умело и красиво использовать данные функции с целью оптимизации размеров баз.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=9194