2008-09-27 10 views
41

Gibt es eine Möglichkeit, zwei Bitmasken in Transact-SQL zu vergleichen, um festzustellen, ob eines der Bits übereinstimmt? Ich habe eine Benutzertabelle mit einer Bitmaske für alle Rollen, denen der Benutzer angehört, und ich möchte alle Benutzer auswählen, die beliebig der Rollen in der gelieferten Bitmaske haben. Unter Verwendung der folgenden Daten sollte eine Rollen-Bitmaske von 6 (Designer + Programmierer) Dave, Charlie und Susan auswählen, aber nicht Nick.Vergleichen zweier Bitmasken in SQL, um zu sehen, ob eines der Bits übereinstimmt

User Table 
---------- 
ID Username Roles 
1 Dave  6 
2 Charlie 2 
3 Susan  4 
4 Nick  1 

Roles Table 
----------- 
ID Role 
1 Admin 
2 Programmer 
4 Designer

Irgendwelche Ideen? Vielen Dank.

Antwort

61

Die Antwort auf Ihre Frage ist Verwenden Sie das Bitwise & wie folgt:

SELECT * FROM UserTable WHERE Roles & 6 != 0 

Die 6 kann für jede Kombination Ihres Bitfelds ausgetauscht werden, wo Sie überprüfen möchten, ob ein Benutzer ein oder mehrere dieser Bits hat. Wenn ich versuche, dies zu validieren, finde ich es normalerweise hilfreich, dies im Binärformat zu schreiben. Ihr Benutzertabelle sieht wie folgt aus:

 1 2 4 
------------------ 
Dave 0 1 1 
Charlie 0 1 0 
Susan 0 0 1 
Nick 1 0 0 

Ihr Test (6) ist diese

 1 2 4 
------------------ 
Test 0 1 1 

Wenn wir durch jede Person gehen, um die bitwaise tun und gegen den Test bekommen wir diese:

 1 2 4 
------------------ 
Dave 0 1 1 
Test 0 1 1 
Result 0 1 1 (6) 

Charlie 0 1 0 
Test 0 1 1 
Result 0 1 0 (2) 

Susan 0 0 1 
Test 0 1 1 
Result 0 0 1 (4) 

Nick 1 0 0 
Test 0 1 1 
Result 0 0 0 (0) 

Das obige sollte demonstrieren, dass alle Datensätze, bei denen das Ergebnis nicht Null ist, eines oder mehrere der angeforderten Flags haben.

Edit: Hier ist der Testfall sollten Sie diese

with test (id, username, roles) 
AS 
(
    SELECT 1,'Dave',6 
    UNION SELECT 2,'Charlie',2 
    UNION SELECT 3,'Susan',4 
    UNION SELECT 4,'Nick',1 
) 
select * from test where (roles & 6) != 0 // returns dave, charlie & susan 

oder

select * from test where (roles & 2) != 0 // returns Dave & Charlie 

oder

select * from test where (roles & 7) != 0 // returns dave, charlie, susan & nick 
+0

Ideal, danke. Vor langer Zeit, um mich daran zu erinnern, wie ich das Problem schließlich gelöst habe, aber das ist mit ziemlicher Sicherheit eine bessere Lösung! – Nick

6

Verwenden Sie das Transact-SQL bitwise AND operator "&" und vergleichen Sie das Ergebnis mit Null. Noch besser, anstatt die Rollen als Bits einer Ganzzahlspalte zu codieren, verwenden Sie boolesche Spalten, eine für jede Rolle. Dann wäre Ihre Abfrage einfach Designer und Programmierer freundlich. Wenn Sie erwarten, dass sich die Rollen während der Lebensdauer Ihrer Anwendung stark ändern, verwenden Sie eine Viele-zu-Viele-Tabelle, um die Zuordnung zwischen Benutzern und ihren Rollen zuzuordnen. Beide Alternativen sind portabler als die Existenz des bitweisen AND-Operators.

+0

Der Operator & zum Vergleichen einer Bitmaske mit einer Rolle funktioniert überprüfen möchten, aber nicht zum Vergleichen einer Bitmaske mit einer anderen Bitmaske. Ich denke, ich muss dynamic sql verwenden und jede Rolle in eine where-Klausel einfügen. – Nick

+1

@Nick - Falsch - siehe meine Antwort. – Jamiec

0

alle Programmierer verwenden, um zu finden:

SELECT * FROM UserTable WHERE Roles & 2 = 2 
+0

OP ist Lookign für Programmierer oder Designer – ScottE

2

SELECT * FROM Tabelle WHERE mask1 & mask2> 0

2

Beispiel:

DECLARE @Mask int 
SET @Mask = 6 

DECLARE @Users TABLE 
(
ID int, 
Username varchar(50), 
Roles int 
) 

INSERT INTO @Users (ID, Username, Roles) 
SELECT 1, 'Dave', 6 
UNION 
SELECT 2, 'Charlie', 2 
UNION 
SELECT 3, 'Susan', 4 
UNION 
SELECT 4, 'Nick', 1 

SELECT * FROM @Users WHERE Roles & @Mask > 0 
+0

Dies ergibt das falsche Ergebnis.Versuchen Sie es mit 4 - Susan & Dave haben beide 4 - aber es gibt nur Susan zurück. – Jamiec

+0

@Jamiec, yep, du hast Recht. Danke - behoben. – ScottE

5
SELECT * FROM UserTable WHERE Roles & 6 > 0