SQL Server Побитовое поведение как С# Enum Flags - программирование
Подтвердить что ты не робот

SQL Server Побитовое поведение как С# Enum Flags

Я использую флаги на перечислениях в С#, и все в порядке, но вы хотите использовать что-то подобное в SQL в следующем сценарии:

Мы хотим вернуть список пользователей, которые являются частью списка или условий, подобных этому:

ConditionOne = 2
ConditionTwo = 4
ConditionThree = 8

и т.д...

У нас будут пользователи с некоторыми из этих условий против них:

User1: 6 (conditions 1 and 2)
User2: 4 (condition 2)
User3: 14 (conditions 1, 2 and 3)

и т.д...

Мы хотим иметь возможность делать запрос, где мы говорим, что все пользователи имеют условие 1, и в этом случае он будет возвращать пользователям 1 и 3, даже если они имеют и другие условия.

Любое понимание было бы очень полезно, использовались только флаги в С#, а не на Sql Server.

4b9b3361

Ответ 1

Пока побитовый оператор, предложенный Джеймсом, будет работать, он не будет очень результативным в реляционной базе данных, особенно когда вы пытаетесь масштабировать миллионы записей. Причина в том, что функции в предложении where не поддаются сопоставлению (они препятствуют поиску индекса).

Что бы я сделал, это создать таблицу, содержащую все возможные комбинации флагов и условий, которые позволят индексу искать условие.

Заполнить флагиConditions. Я использовал один (tinyint). Если вам нужно больше флажков, вы должны расширить этот подход:

CREATE TABLE FlagConditions (
      Flag TINYINT
    , Condition TINYINT
    , CONSTRAINT Flag_Condition PRIMARY KEY CLUSTERED (Condition,Flag)
);

CREATE TABLE #Flags (
      Flag TINYINT IDENTITY(0,1) PRIMARY KEY CLUSTERED
    , DummyColumn BIT NULL);
GO

INSERT #Flags
        ( DummyColumn )
SELECT NULL;
GO 256

CREATE TABLE #Conditions(Condition TINYINT PRIMARY KEY CLUSTERED);

INSERT #Conditions ( Condition )
    VALUES  (1),(2),(4),(8),(16),(32),(64),(128);

INSERT FlagConditions ( Flag, Condition )        
    SELECT
    Flag, Flag & Condition
    FROM #Flags f
    CROSS JOIN #Conditions c
    WHERE Flag & Condition <> 0;

DROP TABLE #Flags;
DROP TABLE #Conditions;

Теперь вы можете использовать таблицу FlagConditions каждый раз, когда вам нужно эффективно искать побитовое условие перечислимого типа:

DECLARE @UserFlags TABLE (Username varchar(10), Flag tinyint);

INSERT @UserFlags(Username, Flag)
    VALUES ('User1',6),('User2',4),('User3',14);

DECLARE @Condition TINYINT = 2;

SELECT u.*
FROM @UserFlags u
INNER JOIN FlagConditions fc ON u.Flag = fc.Flag
WHERE fc.Condition = @Condition;

Это возвращает:

Username   Flag
---------- ----
User1      6
User3      14

Ваш администратор базы данных поблагодарит вас за то, что вы выбрали этот ориентированный ориентированный маршрут.

Ответ 2

Побитовым оператором в SQL является &. Предложение WHERE должно оцениваться как выражение BOOLEAN, например:

create table #temp (id int, username varchar(20), flags int)

insert into #temp values
(1, 'User1', 6),
(2, 'User2', 4),
(3, 'User3', 14)

declare @ConditionOne int = 2

select *
from   #temp
where  flags & @ConditionOne <> 0

drop table #temp

Этот запрос возвращает следующий набор данных:

id          username             flags
----------- -------------------- -----------
1           User1                6
3           User3                14

Ответ 3

У меня была почти такая же проблема, и я мог придумать такое решение:

SELECT  t.value
    , ISNULL(t.C1 + ', ', '') + ISNULL(t.C2, '') + ISNULL(', ' + t.C3, '') AS [type]
FROM
(
    SELECT value,
        CASE WHEN (type & 2) <> 0  THEN 'Type1' END AS C1,
         CASE WHEN (type & 4) <> 0  THEN 'Type2' END AS C2,
         CASE WHEN (type & 8) <> 0  THEN 'Type3' END AS C3
    FROM db.Agent
) t

и результат был следующим:

value       type
----------  ------------------------------------
14          Type1, Type2, Type3
12          Type2, Type3
14          Type1, Type2, Type3