2012-11-30 33 views
16

che uso le bandiere su enumerazioni in C# e tutto è andato bene, ma voglio usare qualcosa di simile in SQL nel seguente scenario:SQL Server Bitwise si comportano come C# Enum Bandiere

Vogliamo restituire un elenco di utenti che sono parte di una lista o di condizioni in questo modo:

ConditionOne = 2 
ConditionTwo = 4 
ConditionThree = 8 

ecc ...

avremo utenti con alcune di queste condizioni contro di loro in questo modo:

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

ecc ...

Vogliamo essere in grado di fare una query in cui diciamo ottenere tutti gli utenti con condizione 1 e in questo scenario che sarebbe tornato utenti 1 e 3, anche se hanno altre condizioni pure.

Qualsiasi intuizione sarebbe molto apprezzata, utilizzato solo Flag in C# e non direttamente in Sql Server.

+4

sarebbe più "SQL-like" per memorizzare questo informazioni in una tabella molti-a-molti. Quindi dovresti memorizzare le righe '(1,1), (1,2), (2,2), (3,1), (3,2), (3,3)' in una tabella separata. Sarebbe più naturale interrogare e offrire opportunità di indicizzazione. –

risposta

11

L'operatore bit a bit in SQL è &. La clausola WHERE deve valutare a un'espressione BOOLEAN, in questo modo:

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 

Questa query restituisce il seguente set di dati:

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

Mentre l'operatore bit a bit suggerito da James funzionerà, non sarà molto performante in un database relazionale, soprattutto quando si tenta di ridimensionare a milioni di record. Il motivo è che le funzioni nella clausola where non sono sargable (impediscono la ricerca di un indice).

Quello che vorrei fare sarebbe creare una tabella che contenga tutte le possibili combinazioni di flag e condizioni, che consentirà una ricerca dell'indice sulla condizione.

Compilare FlagConditions. Ho usato un singolo (minuscolo). Se avete bisogno di più bandiere, si dovrebbe essere in grado di espandere su questo approccio:

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; 

Ora è possibile utilizzare la tabella FlagConditions qualsiasi momento avete bisogno di cercare in modo efficiente su una condizione enum bit a bit:

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; 

Ciò restituisce:

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

vostro DBA vi ringrazierà per andare questo set orientato percorso.

3

avevo quasi lo stesso problema e potrebbe venire con tale soluzione:

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 

e il risultato è stato il seguente:

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