Flaga to liczba z jedną jedynką (lub więcej) opisująca jakąś cechę. Wartości flag nie powinny się pokrywać, chyba że się nie wykluczają – wspólna (pod)cecha. Generalnie mapy bitowe pozwalają na przechowywanie wielu wartości flag czyli wartości poszczególnych bitów mapy (flag, ustawień) w jednej liczbie. Do sprawdzania flag najlepiej jest używać operatory bitowe. 32-bitowy INT to 32 bity informacji na 4 bajtach, 32 informacje logiczne które zapisane z użyciem typu bit zajmowałyby całe 32 bajty.

Operacje logiczne na Mapach bitowych:

  • sprawdzenie ustawienia wybranych bitów:  zmienna & mapa
  • wyzerowanie  wybranych bitów bez zmiany pozostałych bitów: zmienna & mapa
  • ustawianie wybranych bitów na ‘1’ nie zmieniając pozostałych bitów:  zmienna | mapa
  • zmiana wartości wybranego bitu na przeciwne, nie zmieniając pozostałych bitów:  zmienna ^ mapa

Istnieje szereg praktycznych zastosowań:

  1. Bitowe określenie właściwości bazy danych w tabeli systemowej master..sysdatabases.
SELECT 
 name,
 status, 
 CONVERT(BIT, (status & 1 )) as [is_autoclose],
 CONVERT(BIT, (status & 4 )) as [is_select_into_bulkcopy] ,
 CONVERT(BIT, (status & 8 )) as [is_trunc_log_on_chkpt] ,
 CONVERT(BIT, (status & 16 )) as [is_torn_page_detection] ,
 CONVERT(BIT, (status & 32 )) as [is_loading] ,
 CONVERT(BIT, (status & 64 )) as [is_pre_recovery] ,
 CONVERT(BIT, (status & 128 )) as [is_recovering] , 
 CONVERT(BIT, (status & 256 )) as [is_not_recovered] ,
 CONVERT(BIT, (status & 512 )) as [is_offline] ,
 CONVERT(BIT, (status & 1024 )) as [is_read_only] ,
 CONVERT(BIT, (status & 2048 )) as [is_dbo_use_only] ,
 CONVERT(BIT, (status & 4096 )) as [is_single_user] ,
 CONVERT(BIT, (status & 32768 )) as [is_emergency_mode] , 
 CONVERT(BIT, (status & 4194304 )) as [is_autoshrink] , 
 CONVERT(BIT, (status & 1073741824 )) as [is_cleanly_shutdown] 
from master..sysdatabases
  1. Gdybyśmy mieli 15 oddzielnych pól, należałoby każde z nich sumować oddzielnie.
  2. Uprawnienia pochodzące z różnych ról. Zakładamy, że użytkownik ma prawo lub nie ma prawa do czegoś.
  3. W Schedulerze SQL Agenta przy zapisywaniu wielu dni tygodnia
         (1,'Sunday')
		,(2,'Monday')
		,(4,'Tuesday')
		,(8,'Wednesday')
		,(16,'Thursday')
		,(32,'Friday')
		,(64,'Saturday')

W SQL Server pola bitowe przechowujemy w zmiennej typu całkowitego (tinyint, smallint, int, bigint). Taka kolumna niewiele nam mówi, ale rozbijając jej wartość na mapę bitową dostaniemy komplet informacji.

Mapy jako flagi bitowe

Flagi bitowe przyjmują najczęściej wartości równe kolejnym potęgom liczby 2. Najmłodszy bit to 2^0, kolejny 2^1, potem 2^2. Dziesiętnie są to wartości 1, 2, 4, 8, 16 i tak dalej. Jak zatem zmusić SQL Server do użycia tylko flagowych identyfikatorów? Popatrzmy na definicję przykładowej tabeli z flagami, tabelę dni tygodnia z schedulera:

--==============================
-- File:  FlagDemo.sql
-- Author: Dariusz Brejnak
-- Description: 
--=============================
DECLARE @Rights AS TABLE(
	[Flag] [int] NULL,
	[Name] [varchar](128) NULL
)
INSERT INTO @Rights (Flag, NAME)
VALUES	 (1,'Sunday')
		,(2,'Monday')
		,(4,'Tuesday')
		,(8,'Wednesday')
		,(16,'Thursday')
		,(32,'Friday')
		,(64,'Saturday')
DECLARE @i varchar(1000) = ''

DECLARE @FLAG INT

-- tu ustawiamy bity flagi
SET @FLAG =     POWER(2*1,0)  -- 1       'Sunday' 
              + POWER(2*1,1)  -- 2		 'Monday' 
              + POWER(2*0,2)  -- 4		 'Tuesday' 
              + POWER(2*1,3)  -- 8		 'Wednesday' 
			  + POWER(2*0,4)  -- 16		 'Thursday' 
              + POWER(2*1,5)  -- 32		 'Friday' 
	          + POWER(2*1,6)  -- 64		 'Saturday' 
SELECT @FLAG FLAG
SELECT  @i=@i+R.Name+', '  FROM (SELECT @FLAG FLAG) B
			JOIN @Rights R ON B.FLAG & R.Flag = R.Flag
SELECT @i
IF ( (1 & @FLAG) = 1 )   PRINT 'Sunday' 
IF ( (2 & @FLAG) = 2 )   PRINT 'Monday' 
IF ( (4 & @FLAG) = 4 )   PRINT 'Tuesday' 
IF ( (8 & @FLAG) = 8 )   PRINT 'Wednesday' 
IF ( (16 & @FLAG) = 16 ) PRINT 'Thursday' 
IF ( (32 & @FLAG) = 32 ) PRINT 'Friday' 
IF ( (64 & @FLAG) = 64 ) PRINT 'Saturday' 
-------------------------------------------
--Result
Flag=107
Sunday
Monday
Wednesday
Friday
Saturday

Można również posłużyć się poniższym kodem, który wypisze nam wszystkie wartości flag wraz z reprezentacją binarną i dziesiętną:

SELECT
        @FLAG                     as Input
        , cast(@FLAG & 64 as bit) as bit7
        , cast(@FLAG & 32 as bit) as bit6
        , cast(@FLAG & 16 as bit) as bit5
        , cast(@FLAG & 8 as bit)  as bit4
        , cast(@FLAG & 4 as bit)  as bit3
        , cast(@FLAG & 2 as bit)  as bit2
        , cast(@FLAG & 1 as bit)  as bit1
 
        , cast(cast(@FLAG & 64 as bit) as CHAR(1))
        +cast( cast(@FLAG & 32 as bit) as CHAR(1))
        +cast( cast(@FLAG & 16 as bit)  as CHAR(1))
        +cast( cast(@FLAG & 8 as bit)  as CHAR(1))
        +cast( cast(@FLAG & 4 as bit)  as CHAR(1))
        +cast( cast(@FLAG & 2 as bit)   as CHAR(1))
        +cast(cast(@FLAG & 1 as bit)  as CHAR(1)) as binary_string

W rezultacie dla powyższego przykładu otrzymujemy:


Dariusz Brejnak

Od prawie trzydziestu lat jest pasjonatem informatyki, a zwłaszcza dziedzin dotyczących baz danych, hurtowni danych oraz ogólnie rozumianej tematyki BI. Jego druga pasja to fotografia http://dariuszbrejnak.pl