Rambler's Top100

Использование битовых масок в .NET для работы с SQL Server.

Автор: Егоров Никита (C…R…a…S…H)
Опубликовано:  01 September 2006
Уровень:  Решение
Проголосовало 6 читателей
Средняя оценка 4.33

Что же такое битовая маска.

Битовая маска (определение взято Википедии) — определённые данные, которые используются для выбора битов из двоичной строки или числа. Например, для получения значения пятого бита (считая слева) числа 10111011 нужно использовать маску 00001000 и применить операцию побитового алгебрологического «И» (конъюнкцию). В результате получится:

10111011 ^ 00001000 = 00001000

Битовые маски используются, например, для выбора битов из IP-адреса для адресации подсети.

Для чего это все необходимо.

Пусть есть таблица, содержащая телефонные звонки абонентов:

В поле статус хранится вид звонка:

  • Answer
  • Outgoing
  • Abort
  • Cancel

И стоит задача выбрать из таблицы все записи, удовлетворяющие определенному условию статуса. Несколько условий могут использоваться одновременно, например: необходимо выбрать и Abort и Cancel телефонные звонки.

Решение данной проблемы

Создадим тестовый проект консольного приложения и объявим в нем перечисление State, которое помечено атрибутом Flags:

C#

  [Flags] 
  enum State : int 
  { 
      Answer=1, 
      Outgoing=2, 
      Abort=4, 
      Cancel=8 
  } 
  

VB.NET

  <Flags()> _ 
  Enum State As Integer 
      Answer = 1 
      Outgoing = 2 
      Abort = 4 
      Cancel = 8 
  End Enum 
  

Атрибут Flags указывает, что перечисление можно рассматривать как битовое поле, то есть теперь с таким перечислением можно выполнять операции логического ИЛИ (комбинировать значения).

Проведем тестирования такого функционала, для этого создадим функцию, которая будет выводить все возможные комбинации элементов перечисления:

С#

  using System; 
  namespace BitMask 
  { 
      class Program 
      { 
          [Flags] 
          enum State : int 
          { 
              Answer = 1, 
              Outgoing = 2, 
              Abort = 4, 
              Cancel = 8 
          } 
          static void Main(string[] args) 
          { 
              ShowAllСombination(); 
              Console.ReadKey(); 
          } 
          static void ShowAllСombination() 
          { 
              for (int i = 1; i < 16; i++) 
                  Console.WriteLine("{0} - {1}", i, (State)i); 
          } 
      } 
  } 
  

VB.NET

  Module BitMask_VB 
      <Flags()> _ 
      Enum State As Integer 
          Answer = 1 
          Outgoing = 2 
          Abort = 4 
          Cancel = 8 
      End Enum 
      Sub Main() 
          ShowAllСombination() 
          Console.ReadKey() 
      End Sub 
      Sub ShowAllСombination() 
          For i As Integer = 1 To 15 
              Console.WriteLine("{0} - {1}", i, CType(i, State)) 
          Next 
      End Sub 
  End Module 
  

После выполнения данной программы на экране будут отображены все возможные комбинации значений перечисления:

1 - Answer

2 - Outgoing

3 - Answer, Outgoing

4 - Abort

5 - Answer, Abort

6 - Outgoing, Abort

7 - Answer, Outgoing, Abort

8 - Cancel

9 - Answer, Cancel

10 - Outgoing, Cancel

11 - Answer, Outgoing, Cancel

12 - Abort, Cancel

13 - Answer, Abort, Cancel

14 - Outgoing, Abort, Cancel

15 - Answer, Outgoing, Abort, Cancel

Заполнение таблицы тестовыми данными

Для того, что бы заполнить созданную таблицу данными, выполним несколько запросов, которые создадут необходимую для тестирования информацию:

  INSERT INTO Calls VALUES (1,'Иванов','911',1)  --Answer 
  INSERT INTO Calls VALUES (2,'Петров','924',2)  --Outgoing 
  INSERT INTO Calls VALUES (3,'Сидоров','941',4) --Abort 
  INSERT INTO Calls VALUES (4,'Иванов','941',4)  --Abort 
  INSERT INTO Calls VALUES (5,'Сидоров','971',8) --Cancel 
  INSERT INTO Calls VALUES (6,'Иванов','941',8)  --Cancel 
  INSERT INTO Calls VALUES (7,'Иванов','913',8)  --Cancel 
  INSERT INTO Calls VALUES (8,'Петров','943',1)  --Answer 
  INSERT INTO Calls VALUES (9,'Иванов','976',2)  --Outgoing
  

После выполнения запросов в таблице будет содержаться следующая информация:

Теперь все готово для создания хранимой процедуры, которая и будет получать необходимую информацию, используя битовую маску:

  CREATE PROCEDURE dbo.GetCallsByState  
  @State int 
  AS 
  SELECT     ID, Name, Phone, State 
  FROM         Calls 
  WHERE     (State & @State = State) 
  

При выполнении хранимой процедуры происходит следующее:

SQL Server получает двоичное представлениеState и @State, выполняет логическую операцию И (результат операции логическое И будет истинным лишь тогда, когда истинны оба операнда). В результате чего получается число, которое потом сравнивается со значение State.

Пример:

  State = 4
  @State=5 
  

4 в двоичной системе счисления 100, 5 – 101

  100 - State
  101 - @State
  

100 – Результат (если перевести 100 в десятичную систему счисления, то получится 4)

Вызов хранимой процедуры

Для демонстрации использования хранимой процедуры создадим консольное приложение:

C#

  using System; 
  using System.Data; 
  using System.Data.SqlClient; 
  namespace BitMask 
  { 
      class Program 
      { 
          [Flags] 
          enum State : int 
          { 
              Answer = 1, 
              Outgoing = 2, 
              Abort = 4, 
              Cancel = 8 
          } 
          static void Main(string[] args) 
          { 
              Console.WriteLine(); 
              ShowCalls(State.Abort | State.Answer); 
              Console.ReadKey(); 
          } 
          static void ShowCalls(State state) 
          { 
              using (SqlConnection con = new SqlConnection("Data Source=(local);Initial Catalog=tester;Integrated Security=True")) 
              { 
                  con.Open(); 
                  SqlCommand cmd = con.CreateCommand(); 
                  cmd.CommandType = CommandType.StoredProcedure; 
                  cmd.CommandText = "GetCallsByState"; 
                  cmd.Parameters.AddWithValue("@State", state); 
                  SqlDataReader reader = cmd.ExecuteReader(); 
                  while (reader.Read()) 
                  { 
                      Console.WriteLine(reader["ID"]); 
                      Console.WriteLine(reader["Name"]); 
                      Console.WriteLine(reader["Phone"]); 
                      Console.WriteLine((State)reader["State"]); 
                      Console.WriteLine("----------------------"); 
                  } 
                  con.Close(); 
              } 
          } 
      } 
  } 
  

VB.NET

  Imports System.Data.SqlClient 
  Module BitMask_VB 
      <Flags()> _ 
      Enum State As Integer 
          Answer = 1 
          Outgoing = 2 
          Abort = 4 
          Cancel = 8 
      End Enum 
      Sub Main() 
          ShowCalls(State.Abort Or State.Answer) 
          Console.ReadKey() 
      End Sub 
      Sub ShowCalls(ByVal value As State) 
          Using con As New SqlConnection("Data Source=(local);Initial Catalog=tester;Integrated Security=True") 
              con.Open() 
              Dim cmd As SqlCommand = con.CreateCommand() 
              cmd.CommandType = CommandType.StoredProcedure 
              cmd.CommandText = "GetCallsByState" 
              cmd.Parameters.AddWithValue("@State", value) 
              Dim reader As SqlDataReader = cmd.ExecuteReader() 
              While (reader.Read()) 
                  Console.WriteLine(reader("ID")) 
                  Console.WriteLine(reader("Name")) 
                  Console.WriteLine(reader("Phone")) 
                  Console.WriteLine(CType(reader("State"), State)) 
                  Console.WriteLine("----------------------") 
              End While 
              con.Close() 
          End Using 
      End Sub 
  End Module 
  

Результат работы программы будет следующий:

  1 
  Иванов 
  911 
  1 
  ---------------------- 
  3 
  Сидоров 
  941 
  4 
  ---------------------- 
  4 
  Иванов 
  941 
  4 
  ---------------------- 
  8 
  Петров 
  943 
  1 
  ---------------------- 
  

Теперь что бы получить любую комбинацию необходимо объединить несколько значений перечисления.

Используемые источники

  1. http://msdn.microsoft.com/
  2. SQL Server Books Online
  3. http://ru.wikipedia.org
Rambler's Top100    ¦хщЄшэу@Mail.ru