Как обрабатывать много флагов для записи SQL - PullRequest
7 голосов
/ 01 октября 2008

Мне нужен совет, как обрабатывать относительно большой набор флагов в моей таблице SQL2k8.

Два вопроса, терпите меня пожалуйста:)

Допустим, у меня есть 20 флагов, которые я хотел бы сохранить для одной записи.

Например:

CanRead = 0x1 CanWrite = 0x2 CanModify = 0x4 ... и так до окончательного флага 2 ^ 20

Теперь, если я установлю следующую комбинацию одной записи: Permissions = CanRead | CanWrite

Я могу легко проверить, требуется ли для этой записи разрешение, выполнив WHERE (Permissions & CanRead) = CanRead

Это работает.

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

Если я выдам WHERE (Permissions & (CanWrite | CanModify)) = (CanWrite | CanModify), я, очевидно, не получу свою запись с разрешениями, установленными на CanRead | CanWrite

Другими словами, как я могу найти записи, соответствующие ЛЮБОМУ из флагов в моей маске, которые я посылаю процедуре?

Второй вопрос: насколько эффективен SQL в SQL? Было бы лучше на самом деле создать 20-битные поля?

Спасибо за вашу помощь

Ответы [ 8 ]

12 голосов
/ 01 октября 2008

Я предполагаю, что ваш столбец Permissions - Int. Если да, я советую вам поиграть с примером кода, который я приведу ниже. Это должно дать вам четкое представление о том, как работает эта функция.

Declare @Temp Table(Permission Int, PermissionType VarChar(20))

Declare @CanRead Int
Declare @CanWrite Int
Declare @CanModify Int

Select @CanRead = 1, @CanWrite = 2, @CanModify = 4

Insert Into @Temp Values(@CanRead | @CanWrite, 'Read,write')
Insert Into @Temp Values(@CanRead, 'Read')
Insert Into @Temp Values(@CanWrite, 'Write')
Insert Into @Temp Values(@CanModify | @CanWrite, 'Modify, write')
Insert Into @Temp Values(@CanModify, 'Modify')

Select * 
From   @Temp 
Where  Permission & (@CanRead | @CanWrite) > 0

Select * 
From   @Temp 
Where  Permission & (@CanRead | @CanModify) > 0

Когда вы используете логические и, вы получите число с цифрами 1, соответствующим образом основанными на вашем состоянии. Если ничего не найдено, результат будет 0. Если 1 или более условий совпадают, результат будет больше 0.

Позвольте мне показать вам пример.

Предположим, CanRead = 1, CanWrite = 2 и CanModify = 4. Допустимые комбинации:

Modify Write Read Permissions
------ ----- ---- -----------
  0       0    0   Nothing
  0       0    1   Read
  0       1    0   Write
  0       1    1   Read, Write
  1       0    0   Modify
  1       0    1   Modify, Read
  1       1    0   Modify, Write
  1       1    1   Modify, Write, Read

Теперь предположим, что вы хотите проверить на чтение или изменение. Из вашего приложения вы могли бы перейти (CanRead | CanModify). Это будет 101 (в двоичном виде). ​​

Во-первых, давайте проверим это по строке в таблице, которую ТОЛЬКО прочитал.

   001 (Row from table)
&  101 (Permissions to test)
------
   001 (result is greater than 0)

Теперь давайте проверим строку, в которой есть только Write.

   010 (Row from table)
&  101 (Permission to test)
------
   000 (result = 0)

Теперь проверьте его для строки, которая имеет все 3 разрешения.

   111 (Row from table)
&  101 (Permission to test)
------
   101 (result is greater than 0)

Надеюсь, вы видите, что если результат операции AND приводит к значению = 0, то ни одно из протестированных разрешений не применяется к этой строке. Если значение больше 0, то присутствует хотя бы одна строка.

11 голосов
/ 01 октября 2008

Не надо. Это похоже на сохранение строки CSV в мемо-поле и поражение цели базы данных.

Используйте логическое (битное) значение для каждого флага. В этом конкретном примере вы найдете все, что можно прочитать, а также написать или изменить:

WHERE CanRead AND (CanWrite OR CanModify)

Простой чистый SQL без хитрых хаков. Дополнительные 7 бит, которые вы тратите на каждый флаг, не стоят головной боли.

6 голосов
/ 01 октября 2008

А как же

WHERE (Permissions & CanWrite) = CanWrite 
OR (Permissions & CanModify) = CanModify

3 голосов
/ 01 октября 2008

Разве это не так просто, как ...

WHERE (Permissions & ( CanWrite | CanModify )) > 0

... поскольку любое значение «бит», установленное в 1, приведет к ненулевому значению для оператора «&».

Уже поздно, и я собираюсь домой, так что мой мозг может работать неэффективно.

3 голосов
/ 01 октября 2008

ГДЕ (разрешения и CanWrite) = CanWrite ИЛИ (Права доступа и CanModify) = CanModify

Я думаю

1 голос
/ 01 октября 2008

Было бы значительно лучше иметь другую модель разрешений.

20 флагов указали бы мне, что требуется переосмысление, большинство систем регистрации могут обойтись с 12 базовыми флагами и ACLS - возможно, имея отдельную таблицу, которая просто предоставляет разрешения, или группируя объекты или средства доступа для обеспечения другого контроля.

Я ожидаю, что выбор будет быстрее иметь 20 отдельных полей - но я бы не добавил 20 полей для производительности.

- обновление -

исходный запрос, записанный как

 WHERE (Permissions & ( CanWrite | CanModify )) > 0

будет достаточно, однако кажется, что то, что у вас есть в базе данных, представляет собой набор атрибутов, которые может иметь сущность. В этом случае единственный разумный (в терминах базы данных) способ сделать это - установить отношение «один ко многим» с таблицей атрибутов.

0 голосов
/ 02 октября 2008

Делайте это только в том случае, если вы запрашиваете другой ключ.

Не делайте этого, если вы запрашиваете комбинации флагов. Индекс по этому столбцу не поможет вам в целом. Вы будете ограничены сканированием таблицы.

0 голосов
/ 01 октября 2008

Нет, это не сработает.

Я отправляю только одну маску на процедуру

Что-то вроде @filter, которое в C # я заполняю @filter = CanModify | CanWrite

Итак, процедура получает значение ИЛИ в виде фильтра.

Да, и, кстати, это НЕ модель разрешения, я использую это только в качестве примера.

У меня действительно есть около 20 уникальных флагов, которые может иметь мой объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...