Click here to Skip to main content
Click here to Skip to main content

Representing Multiple Selections using a Single Value in Database Tables

By , 13 Nov 2009
 

Introduction

Usually, boolean values are stored as bit fields in database tables. If we require only a few boolean values to be stored in the table, it would be good to have them as separate bit fields. But consider a situation where we want to store some series of boolean values in the database; I have a good idea to avoid creating fields for each boolean value in the table. For example, choosing weekdays, months in scheduling kind of problems, attendance for 8 hours of a day in school/college management applications, list of colours, card-suit values such as clubs, diamonds, hearts, spades, or any kind of finite or non-finite enumerations can be represented in the database with this encoding technique.

Background

The idea is very simple, and needs bitwise interpretation logic for extracting each of the required bit fields out from the encoded value and conflating from all the bit values back to the encoded value.

Before deciding the size of the field for storing the encoded value, identify whether the list of selections is finite or non-finite. If the list of selections is finite, then the implementation is pretty simple; the size of the encoded value will straightaway be the number of items in the list. In weekdays-selection, for 7 days, it is enough to have a byte type data field in the table to hold the whole week. For 12 months, we need a 16 bit data field.

But, if the list is non-finite, then we must do it with some predictions. For example, assume that our company presently releases 5 newsletters and users are provided with the choice of subscribing these newsletters. We may need a byte to hold the encoded value for each user's subscriptions. It can hold up to 8 newsletter subscriptions. But, in future, if the company comes out with more than 8 newsletters, our encoded value shall require more than 8 bits. Naturally, somehow we can predict the maximum possible count of newsletters. These can be a maximum of 16 or even 32. It's better if we start allotting the least significant bits (LSB) to the existing/identified newsletters in the encoded value.

Implementation of Weekdays Selection in C#

Let us implement the selection of weekdays using this technique. For alert/alarm kind of applications, this Weekdays class can be used. This class contains 7 boolean type properties for 7 days in a week to represent whether the corresponding day is required to be included in the schedule or not. In the code below, the AllDays property sets the value of each weekday by interpreting the encoded value read from the database table which shall be used to show in the checkboxes; similarly, it gets the independently selected weekdays in a single encoded value, which can be stored in the table in a single field. Thanks to Paulo Zemek for his advice to change the values used for bitwise operations to enumerated data with a flags attribute.

public class Weekdays
{
    // Enumerated data with Flags for setting 
    // each corresponding bit among 7 bits
    [Flags]
    enum weekday
    { 
        Sun = 1,
        Mon = 2,
        Tue = 4,
        Wed = 8,
        Thu = 16,
        Fri = 32,
        Sat = 64
    };
    
    // Store boolean values for seven days
    private bool sunday;
    private bool monday;
    private bool tuesday;
    private bool wednesday;
    private bool thursday;
    private bool friday;
    private bool saturday;
    
    // Expose the properties outside
    public bool Sunday
    {
        get { return sunday; }
        set { sunday = value; }
    }
    public bool Monday
    {
        get { return monday; }
        set { monday = value; }
    }
    public bool Tuesday
    {
        get { return tuesday; }
        set { tuesday = value; }
    }
    public bool Wednesday
    {
        get { return wednesday; }
        set { wednesday = value; }
    }
    public bool Thursday
    {
        get { return thursday; }
        set { thursday = value; }
    }
    public bool Friday
    {
        get { return friday; }
        set { friday = value; }
    }
    public bool Saturday
    {
        get { return saturday; }
        set { saturday = value; }
    }
    
    // Making all the days to be jointly interpreted as a single byte
    public byte AllDays
    {
        get
        {
            byte Value = 0;
            // from LSB to MSB - Sunday, Monday, Tuesday, 
            // Wednesday, Thursday, Friday, Saturday
            if (sunday) Value |= (byte)weekday.Sun ;
            if (monday) Value |= (byte)weekday.Mon ;
            if (tuesday) Value |= (byte)weekday.Tue;
            if (wednesday) Value |= (byte)weekday.Wed;
            if (thursday) Value |= (byte)weekday.Thu;
            if (friday) Value |= (byte)weekday.Fri;
            if (saturday) Value |= (byte)weekday.Sat;
            return Value;
        }
        set
        {
            // Extract the corresponding bits for each weekday
            // into bool properties of the class
            sunday = ((value & (byte)weekday.Sun ) != 0 );
            monday = ((value & (byte)weekday.Mon) != 0);
            tuesday = ((value & (byte)weekday.Tue) != 0);
            wednesday = ((value & (byte)weekday.Wed) != 0);
            thursday = ((value & (byte)weekday.Thu) != 0);
            friday = ((value & (byte)weekday.Fri) != 0);
            saturday = ((value & (byte)weekday.Sat) != 0);
        }
    }
}

Using the Code

I have developed a demo application for using this. The screenshot shows the form and its controls in its running mode.

bitfields.png

The code below is to exemplify the usage of the Weekdays class:

public partial class frmDemo : Form
{
    // Weekdays object
    Weekdays weekdays = null;
    public frmDemo()
    {
        InitializeComponent();
    }
    
    private void frmDemo_Load(object sender, EventArgs e)
    {
        // Create a Weekdays object
        weekdays = new Weekdays();
    }
    
    // Get the current byte value for the checkbox selections and
    // and update the checkbox values
    private void btnSetByteValue_Click(object sender, EventArgs e)
    {
        weekdays.AllDays = Convert.ToByte(txtByteIn.Text);
        PopulateCheckBoxes();
    }
    
    // Populate checkbox values from the weekdays object
    private void PopulateCheckBoxes()
    {
        chkSunday.Checked = weekdays.Sunday;
        chkMonday.Checked = weekdays.Monday;
        chkTuesday.Checked = weekdays.Tuesday;
        chkWednesday.Checked = weekdays.Wednesday;
        chkThursday.Checked = weekdays.Thursday;
        chkFriday.Checked = weekdays.Friday;
        chkSaturday.Checked = weekdays.Saturday;
    }
    
    // Get the checked/unchecked values
    // from checkboxes into the Weekdays object
    private void GetCheckBoxesSelections()
    {
        weekdays.Sunday = chkSunday.Checked;
        weekdays.Monday = chkMonday.Checked;
        weekdays.Tuesday = chkTuesday.Checked;
        weekdays.Wednesday = chkWednesday.Checked;
        weekdays.Thursday = chkThursday.Checked;
        weekdays.Friday = chkFriday.Checked;
        weekdays.Saturday = chkSaturday.Checked;
    }
    
    // Checkbox values are collected in the weekdays object and
    // displays the resultant byte in the output textbox
    private void btnGetByteValue_Click(object sender, EventArgs e)
    {
        GetCheckBoxesSelections();
        txtByteOut.Text = weekdays.AllDays.ToString();
    }
}

Manipulating selections directly from the database

SQL Server supports the following bitwise operations:

  • & (Bitwise AND)
  • | (Bitwise OR)
  • ^ (Bitwise Exclusive OR)

The operands can be int, smallint, or tinyint, except image data types. See the list of supporting datatypes here. So, we can select the records with particular selections directly from the query. For instance, in our weekdays selection example program:

DECLARE @Sun as smallint;
DECLARE @Mon as smallint; 
DECLARE @Tue as smallint; 
DECLARE @Wed as smallint;
DECLARE @Thu as smallint;
DECLARE @Fri as smallint;
DECLARE @Sat as smallint;
SET @Sun = 1;
SET @Mon = 2;
SET @Tue = 4;
SET @Wed = 8;
SET @Thu = 16;
SET @Fri = 32;
SET @Sat = 64;
SELECT * FROM ScheduleForYou WHERE (WeekdaySelection & @Sun)<>0

The above query selects the records only when Sunday is selected in it.

One important and interesting feature of this technique is you can select the records with only Sunday selected. Wow! this avoids multiple conditions applied in a query; i.e., when you have separate fields in the table for each day, you have to apply the conditions like this:

WHERE Sunday=true AND Monday=false AND Tuesday=false AND Wednesday=false 
                  AND Thursday=false AND Friday=false AND Saturday=false

But, the following query achieves this in our table:

SELECT * FROM ScheduleForYou WHERE (WeekdaySelection ^ @Sun)=0

The bitwise XOR does this work, and thanks to CodeProject member bquick for making me explore this via his question.

Advantages and disadvantages of this technique

The advantages include:

  1. Avoids independent, clumsy bit fields in the table which will be difficult to manage while passing them from application via Stored Procedures and queries.
  2. To some extent, the addition of new selection items doesn't require altering tables or related Stored Procedures.
  3. Makes the coding easy for developing user interfaces.

Of course, there are a few disadvantages too:

  1. Interpreting the values directly in the database table is not easy.
  2. Enforcing field level access rights is missing.
  3. Most significant unused bits are wasted (if any; and the right choice of data type for the field is a must).

History

  • 9th November, 2009: Initial post.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

P. Ganesh Kumar
Engineer International Flavours & Fragrances India Pvt. Ltd
India India
Member
Ganesh Kumar has done his Bachelor's degree in Computer Science at Bharathiar University, Coimbatore, Master's degree in Computer Applications at IGNOU and M.Tech in Computer Science & Engineering at Dr. MGR University, Chennai, India.
 
He is having hands-on experience in Algorithm implementation, Application design/development for Servers, Desktop and Single Board Computer devices in .Net C#, C, C++.
 
Interested in developing & customizing algorithms.
 
His hobbies include Blogging, Listening to Music and Singing.
 
 Home:        http://www.technicalganesh.com/

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralBits!memberBit-Smacker17 Nov '09 - 6:44 
I remember seeing this multi-bool storage technique being implemented on the Commodore 64 computer to conserve RAM and processor cycles. It was typically used for security-level authorization in bulletin-board system software.
 
By checking for a bit in a user's security value, you could determine whether they had access to a specific granular feature of the software.
 
It's nice to see an efficient technique still being implemented in such an inefficient world that is spoiled by fast CPUs, cheap RAM, and cheap hard drives!
GeneralRe: Bits!memberP. Ganesh Kumar25 Nov '09 - 1:25 
Thanks for your reply.
 
I think the system may be a super computer or a small one like what you said Commodore 64, every penny saved is a penny earned. Let's try to achieve the efficient and convenient solutions for this kind of generic problems.
 
P. Ganesh Kumar

GeneralIts called Bit fieldmembersahej17 Nov '09 - 6:27 
http://en.wikipedia.org/wiki/Bit_field[^] Sleepy | :zzz:
QuestionWhat about "And" operator in weedays class?memberkltong16 Nov '09 - 15:32 
"And" operator work really well for Bitwise.
Suggestion to combine all days properties to one.
 
public class Weekdays
{
// Store boolean values for seven days
private int days;
 

// Getday
public function Getweekday(byval value as weekday) as boolean {
return (value And days)
}
//Set day
public Sub Getweekday(byval value as weekday){
days+=value
}
 
SuGaR

GeneralNot suitable for indexesmemberwarny13 Nov '09 - 5:51 
Hi,
I often use this method, however, there's one drawback : you can't use indexes.
You'll say that a two value field shouldn't be indexed, which is mostly right. But if you consider a flag for closing a record, the time will make the ration between closed and non closed records more and more unbalanced which will be suitable for an index.
 

Another thing, with SQL, these agregate field can do great things.
 
- What about selecting all records which contains both monday and tuesday :
WHERE dayfield & (@monday + @tuesday) = @monday + @tuesday
 
- And what about selecting every record that contains monday but not tuesday :
WHERE dayfield & (@monday + @tuesday) = @monday
 
- You can also imagine selecting records that contains monday and tuesday, or tuesday and wednesday but not monday and wednesday
WHERE dayfield & (@monday + @tuesday + @wednesday) IN (@monday + @tuesday, @tuesday + @wednesday )
GeneralRe: Not suitable for indexesmemberP. Ganesh Kumar14 Nov '09 - 22:38 
All the requirements you have given can be possible with this method (workout and form the SQL queries by referring the examples given by me in the article).
 
Its good for indexing since the table has only one field for all the days only one index is enough (unlike indexing every day-field).
 
Finally, to all, this weekdays example is for demonstrating the technique. Its really worthy applying for the other requirements I explained (Newsletter subscription) which has not only 7 selections but more than that.
 
For an instance, are you going to have 35 bit fields in the table and going to index all of them? think
 
P. Ganesh Kumar

GeneralRe: Not suitable for indexesmembervoloda218 Nov '09 - 2:38 
I don't think this way is better - if you would select all records which have set monday and tuesday in table with milions of rows, it will result in many bit operations on the field instead of simple comparison against 1 or 0. So you will save some space in database but you may lost speed of the database.
 
On the other hand using indexes on bit fields is not very efficient at all (think - is it really usefull to have index on field with only 2 values?)
 
---
Voloda

GeneralRe: Not suitable for indexesmemberP. Ganesh Kumar25 Nov '09 - 1:17 
I am accepting that you have caught a rabbit with three legs and that could be a handicapped one.
 
This is a speedy approach with respect to database than what you are telling!
 
Finally with the technique I proposed, you cannot save space. I am not looking for saving space. I am looking for a convenient way of overcoming clumsy bit fields.
 
P. Ganesh Kumar

GeneralMy vote of 1membervoloda212 Nov '09 - 8:21 
1. There is nothing about the database.
 
2. There is unnecessary type casting on enum, why isn't it inherited from byte?
GeneralRe: My vote of 1memberP. Ganesh Kumar14 Nov '09 - 22:43 
Answer for your questions!
 
1. Please read the article again!
 
2. Thats great.... please do it for us!.... I just gave the idea to store multiple bit fields in the data-table with single field
 
Thanks for your Vote!...
 
P. Ganesh Kumar

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 13 Nov 2009
Article Copyright 2009 by P. Ganesh Kumar
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid