2012-11-30 16 views
16

używam flagi na teksty stałe w C# i wszystko jest w porządku, ale chcą używać czegoś podobnego w SQL w następujący scenariusz:SQL Server bitowe zachowywać jak C# Enum Flagi

Chcemy, aby powrócić do listy użytkowników, które są część listy lub warunków tak:

ConditionOne = 2 
ConditionTwo = 4 
ConditionThree = 8 

etc ...

będziemy mieć użytkownikom niektóre z tych warunków wobec nich tak:

User1: 6 (conditions 1 and 2) 
User2: 4 (condition 2) 
User3: 14 (conditions 1, 2 and 3) 

etc ...

Chcemy być w stanie wykonać kwerendy, gdzie mówimy uzyskać wszystkich użytkowników z warunku 1 i w tym przypadku byłoby powrócić użytkowników 1 i 3, chociaż mają inne warunki, jak również.

Wszelkie wglądy byłyby bardzo mile widziane, używane tylko flagi w języku C#, a nie bezpośrednio w serwerze Sql.

+4

To byłoby bardziej "SQL-like", aby zapisać ten informacje w tabeli wielu do wielu. Więc przechowujesz wiersze '(1,1), (1,2), (2,2), (3,1), (3,2), (3,3)' w osobnej tabeli. Zapewniłoby to bardziej naturalne zapytania i oferuje możliwości indeksowania. –

Odpowiedz

11

Operatorem bitowym w języku SQL jest &. Klauzula WHERE musi dawać w wyniku ekspresji BOOLEAN, tak:

create table #temp (id int, username varchar(20), flags int) 

insert into #temp values 
(1, 'User1', 6), 
(2, 'User2', 4), 
(3, 'User3', 14) 

declare @ConditionOne int = 2 

select * 
from #temp 
where flags & @ConditionOne <> 0 

drop table #temp 

Ta kwerenda zwraca następujący zestaw danych:

id   username    flags 
----------- -------------------- ----------- 
1   User1    6 
3   User3    14 
14

Podczas gdy operator bitowe sugerowane przez Jamesa będzie działać, to nie będzie bardzo wydajność w relacyjnej bazie danych, zwłaszcza gdy próbujesz skalować do milionów rekordów. Powodem jest to, że funkcje w klauzuli where nie są sargable (uniemożliwiają wyszukiwanie indeksu).

Co chciałbym zrobić, to stworzyć tabelę, która zawiera wszystkie możliwe kombinacje flag i warunków, które pozwolą indeksowi na szukanie warunku.

Zapełnij warunki flagi. Użyłem singla (tinyint). Jeśli potrzebujesz więcej Flagi, powinieneś być w stanie rozwijać się na tym podejściu:

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; 

Teraz można użyć tabeli FlagConditions dowolny czas potrzebny do efektywnego poszukiwania na stanie enum bitowe:

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; 

Ta funkcja zwraca:

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

Twoja DBA będzie Ci wdzięczna za udanie się na tę nastawioną trasę.

3

miałem prawie ten sam problem i może pochodzić z takiego rozwiązania:

SELECT t.value 
    , ISNULL(t.C1 + ', ', '') + ISNULL(t.C2, '') + ISNULL(', ' + t.C3, '') AS [type] 
FROM 
(
    SELECT value, 
     CASE WHEN (type & 2) <> 0 THEN 'Type1' END AS C1, 
     CASE WHEN (type & 4) <> 0 THEN 'Type2' END AS C2, 
     CASE WHEN (type & 8) <> 0 THEN 'Type3' END AS C3 
    FROM db.Agent 
) t 

a wynik był następujący:

value  type 
---------- ------------------------------------ 
14   Type1, Type2, Type3 
12   Type2, Type3 
14   Type1, Type2, Type3