Сравнение двух битовых масок в SQL, чтобы увидеть, соответствуют ли какие-либо из битов

есть ли способ сравнить две битовые маски в Transact-SQL, чтобы увидеть, соответствуют ли какие-либо из битов? У меня есть таблица пользователей с битовой маской для всех ролей, к которым принадлежит пользователь, и я хотел бы выбрать всех пользователей, у которых есть любой ролей в поставляемой битовой маске. Поэтому, используя приведенные ниже данные, битовая маска ролей 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 ответов


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

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

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

        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) 

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

Edit: вот тестовый случай, если вы хотите проверить это

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

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


SELECT * FROM UserTable WHERE Roles & 6 > 0

выберите * из таблицы, где mask1 & mask2 > 0


пример:

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

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

SELECT * FROM UserTable WHERE Roles & 2 = 2