Сравнивая две битовые маски в SQL, чтобы увидеть, совпадают ли какие-либо биты - PullRequest
46 голосов
/ 27 сентября 2008

Есть ли способ сравнения двух битовых масок в Transact-SQL, чтобы определить, совпадает ли какой-либо из битов? У меня есть таблица User с битовой маской для всех ролей, к которой принадлежит пользователь, и я хотел бы выбрать всех пользователей, которые имеют any ролей в поставляемой битовой маске. Поэтому, используя приведенные ниже данные, битовую маску ролей 6 (дизайнер + программист) должны выбрать Дейв, Чарли и Сьюзан, но не Ник.

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

Есть идеи? Спасибо.

Ответы [ 6 ]

76 голосов
/ 13 февраля 2012

Ответ на ваш вопрос заключается в использовании побитового &, например:

SELECT * FROM UserTable WHERE Roles & 6 != 0

6 можно заменить на любую комбинацию вашего битового поля, где вы хотите проверить, что у любого пользователя есть один или несколько из этих битов. Когда я пытаюсь это проверить, я обычно нахожу полезным записать это в двоичном виде. Ваша пользовательская таблица выглядит так:

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

Ваш тест (6) это

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

Если мы пройдем через каждого человека, выполняющего битовую атаку, и, пройдя тест, мы получим следующее:

        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) 

Вышеприведенное должно продемонстрировать, что любые записи, в которых результат не равен нулю, имеют один или несколько запрошенных флагов.

Редактировать: вот контрольный пример, если вы хотите проверить это

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

или

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

или

select * from test where (roles & 7) != 0 // returns dave, charlie, susan & nick
6 голосов
/ 27 сентября 2008

Используйте Transact-SQL побитовый оператор AND"&" и сравните результат с нулем. Еще лучше, вместо того, чтобы кодировать роли как биты целочисленного столбца, используйте логические столбцы, по одному для каждой роли. Тогда ваш запрос будет просто удобен для дизайнеров и программистов. Если вы ожидаете, что роли сильно изменятся за время существования вашего приложения, используйте таблицу «многие ко многим», чтобы сопоставить связь между пользователями и их ролями. обе альтернативы являются более переносимыми, чем полагаться на существование оператора побитового И.

5 голосов
/ 13 февраля 2012
SELECT * FROM UserTable WHERE Roles & 6 > 0
2 голосов
/ 25 августа 2010

пример:

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
2 голосов
/ 25 августа 2010

ВЫБРАТЬ * ИЗ таблицы ГДЕ mask1 & mask2> 0

0 голосов
/ 27 сентября 2008

Чтобы найти всех программистов, используйте:

SELECT * FROM UserTable WHERE Roles & 2 = 2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...