Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL Server Bitwise Processing like C# Enum Flags

How can one use in SQL Server the processing of the Flags such as on enums in C#?


For example, how would one return a list of users that are part of a list or conditions like so:

ConditionAlpha = 2 ConditionBeta  = 4 ConditionGamma = 8 

...

Then there will be users with some of these conditions against them like so:

User1: 6 (conditions Alpha and Beta) User2: 4 (condition Beta) User3: 14 (conditions Alpha, Beta and Gamma) 

...

We want to be able to do a query where we say get all users with the first condition Alpha and in this scenario it would return users 1 and 3 even though they have other conditions as well.

like image 238
user351711 Avatar asked Nov 30 '12 12:11

user351711


People also ask

What are SQL Bitwise Operators?

Bitwise operators perform bit manipulations between two expressions of any of the data types of the integer data type category. Bitwise operators convert two integer values to binary bits, perform the AND , OR , or NOT operation on each bit, producing a result. Then converts the result to an integer.

What is SQL Bitmask?

Bitmasking is an exercise of compiling multiple values that are normally represented in a number of data types to a single computer word. In a previous example, we used the binary data type to see how bit manipulation is represented visually.

Can we use & operator in SQL Server?

In SQL, the AND & OR operators are used for filtering the data and getting precise results based on conditions. The SQL AND & OR operators are also used to combine multiple conditions. These two operators can be combined to test for multiple conditions in a SELECT, INSERT, UPDATE, or DELETE statement.

What is SQL Server optimizer?

The SQL Server Query Optimizer is a cost-based optimizer. Each possible execution plan has an associated cost in terms of the amount of computing resources used. The Query Optimizer must analyze the possible plans and choose the one with the lowest estimated cost.


2 Answers

The bitwise operator for checking whether a flag is set in SQL is &. The WHERE clause needs to evaluate to a BOOLEAN expression, like this:

create table #temp (id int, username varchar(20), flags int)  insert into #temp values (1, 'User1', 6 /* (2 | 4) */), (2, 'User2', 4), (3, 'User3', 14 /* (2 | 4 | 8) */)  declare @ConditionOne int = 2  select * from   #temp where  flags & @ConditionOne <> 0  declare @ConditionTwo int = 4  select * from   #temp where  flags & @ConditionTwo <> 0  declare @ConditionThree int = 8  select * from   #temp where  flags & @ConditionThree <> 0  drop table #temp 

These queries return the following resultsets:

id          username             flags ----------- -------------------- ----------- 1           User1                6 3           User3                14  id          username             flags ----------- -------------------- ----------- 1           User1                6 2           User2                4 3           User3                14  id          username             flags ----------- -------------------- ----------- 3           User3                14 
like image 119
James L. Avatar answered Sep 19 '22 09:09

James L.


While the bitwise operator suggested by James will work, it will not be very performant in a relational database, especially when you try to scale to millions of records. The reason is that functions in the where clause are not sargable (they prevent an index seek).

What I would do would be create a table which contains all possible combinations of flags and conditions, which will enable an index seek on the condition.

Populate FlagConditions. I used a single (tinyint). Should you need more Flags, you should be able to expand on this approach:

CREATE TABLE FlagConditions (       Flag TINYINT     , Condition TINYINT     , CONSTRAINT Flag_Condition PRIMARY KEY CLUSTERED (Condition,Flag) );  CREATE TABLE #Flags (       Flag TINYINT IDENTITY(0,1) PRIMARY KEY CLUSTERED     , DummyColumn BIT NULL); GO  INSERT #Flags         ( DummyColumn ) SELECT NULL; GO 256  CREATE TABLE #Conditions(Condition TINYINT PRIMARY KEY CLUSTERED);  INSERT #Conditions ( Condition )     VALUES  (1),(2),(4),(8),(16),(32),(64),(128);  INSERT FlagConditions ( Flag, Condition )             SELECT     Flag, Flag & Condition     FROM #Flags f     CROSS JOIN #Conditions c     WHERE Flag & Condition <> 0;  DROP TABLE #Flags; DROP TABLE #Conditions; 

Now you can use the FlagConditions table any time you need to efficiently seek on an enum bitwise condition:

DECLARE @UserFlags TABLE (Username varchar(10), Flag tinyint);  INSERT @UserFlags(Username, Flag)     VALUES ('User1',6),('User2',4),('User3',14);  DECLARE @Condition TINYINT = 2;  SELECT u.* FROM @UserFlags u INNER JOIN FlagConditions fc ON u.Flag = fc.Flag WHERE fc.Condition = @Condition; 

This returns:

Username   Flag ---------- ---- User1      6 User3      14 

Your DBA will thank you for going this set oriented route.

like image 40
brian Avatar answered Sep 19 '22 09:09

brian