I’ve seen bit-product columns from time-to-time, mostly in SQL Server 2000 system tables, but it’s never been something I’ve had to work with. And when I’ve needed to, I’ve known how to figure out which options are selected, i.e. a bit product of 9 means options 8 and 1 are selected. If you’ve ever taken a look at the [status] column on the sysdatabases table (SQL 2000), you’ll know what I’m talking about.
What I’ve never known how to do, until recently, was calculate these options programmatically. That’s why, when I noticed the [freq_interval] on the sysschedules table was a bit-product column, I decided to spend a little time figuring it out. Fortunately for me, a couple of my awesome co-workers, Jeff M. and Jason H., have worked with this before and were able to explain it to me. And, it turns out, it’s actually quite easy.
Let me back up a few steps in case you’re not familiar with this topic. If you check out the Books Online entry for the sysschedules table (2005), you’ll notice the following statement:
freq_interval is one or more of the following:
1 = Sunday
2 = Monday
4 = Tuesday
8 = Wednesday
16 = Thursday
32 = Friday
64 = Saturday
When I looked at the actual value in the table, the schedule has a [freq_interval] value of 42, which is the sum of the bit values for the days selected.
If there were more than 7 options, the bit values would continue to double, i.e. 128, 256, etc. And regardless of how many bit values you select, you’re guaranteed one and only one possible answer, as the sum of all previous bit values will never exceed the next bit value:
1 + 2 = 3
1 + 2 + 4 = 7
1 + 2 + 4 + 8 = 15
Knowing this, I’m able to retrieve the values manually: I start with the highest bit value that does not exceed 42, then subtract it; I repeat until I’m left with 0.
42 – 32 = 10
10 – 8 = 2
2 – 2 = 0
That means my job is scheduled to run on Friday’s (32), Wednesday’s (8), and Monday’s (2).
Now how do I do this with T-SQL? SQL Server provides an operator specifically for this task: the bitwise AND operator (&). For now, I’m going to skip the “why” behind this and just get to the practical application. If you’re interested in the “why,” let me know and I’ll write a follow-up post on binary and logical AND and OR operations.
For example, to use the bitwise AND to find out which days are selected…
Select 42 & 1 As 'Sunday' , 42 & 2 As 'Monday' , 42 & 4 As 'Tuesday' , 42 & 8 As 'Wednesday' , 42 & 16 As 'Thursday' , 42 & 32 As 'Friday' , 42 & 64 As 'Saturday';
… will return …
Sunday Monday Tuesday Wednesday Thursday Friday Saturday ----------- ----------- ----------- ----------- ----------- ----------- ----------- 0 2 0 8 0 32 0
If the result is not equal to zero, then that day is selected. Easy as key lime pie, right?
Now let’s take it a step further and create our own working example. Let’s say we’re going to track the characteristics of various objects in a single bit-product column (note: this is not necessarily the best way to accomplish this in the real world, but it’s a good illustration). First, set up a table to use in our example. This table will have a column, [attributes], which will hold the sum of our bit values.
Create Table myTable ( id int identity(1,1) , item varchar(10) , attributes int ); Insert Into myTable Select 'Broccoli', 200 Union All Select 'Tomato', 193 Union All Select 'Car', 276 Union All Select 'Ball', 292;
Next, we’re going to create a table variable that holds characteristics and their values. We’ll then join these two tables together to see which attributes exist for each item.
Declare @statusLookup Table ( attribute int , value varchar(10) ); Insert Into @statusLookup Select 1, 'Red' Union All Select 4, 'Blue' Union All Select 8, 'Green' Union All Select 16, 'Metal' Union All Select 32, 'Plastic' Union All Select 64, 'Plant' Union All Select 128, 'Edible' Union All Select 256, 'Non-Edible'; Select a.item, b.value From myTable a Cross Join @statusLookup b Where a.attributes & b.attribute <> 0 Order By a.item , b.value
You should get this result:
item value ---------- ---------- Ball Blue Ball Non-Edible Ball Plastic Broccoli Edible Broccoli Green Broccoli Plant Car Blue Car Metal Car Non-Edible Tomato Edible Tomato Plant Tomato Red
Great, now we know broccoli is edible! Let’s apply a little XML to clean up the results…
Select a.item , Replace( Replace( Replace(( Select value From @statusLookup AS b Where a.attributes & b.attribute <> 0 Order By b.value For XML Raw) , '"/><row value="', ', '), '<row value="', ''), '"/>', '') As 'attributes' From myTable a Order By a.item;
item attributes ---------------------------------------- Ball Blue, Non-Edible, Plastic Broccoli Edible, Green, Plant Car Blue, Metal, Non-Edible Tomato Edible, Plant, Red
Voila! There you have it, how to use the bitwise AND (&) operator to retrieve multiple values from a bit-product column. Pretty neat stuff!
Special thanks to Jeff M. and Jason H. for their assistance.
Michelle Ufford (aka SQLFool)