Сравнение двух битовых масок в SQL, чтобы увидеть, совпадает ли какой-либо из битов
-
02-07-2019 - |
Вопрос
Есть ли способ сравнить две битовые маски в 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
Есть какие-нибудь идеи?Спасибо.
Решение
Ответ на ваш вопрос заключается в использовании побитового &
вот так:
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
Другие советы
Используйте 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