SQL Server Bitwise的行为类似于C#Enum Flags

use*_*711 27 sql sql-server bitwise-operators

我在C#中的枚举上使用了Flags,一切都很好但是在以下场景中想要在SQL中使用类似的东西:

我们想要返回属于列表或条件的用户列表,如下所示:

ConditionOne = 2
ConditionTwo = 4
ConditionThree = 8
Run Code Online (Sandbox Code Playgroud)

等等...

我们将为用户提供一些针对他们的条件,如下所示:

User1: 6 (conditions 1 and 2)
User2: 4 (condition 2)
User3: 14 (conditions 1, 2 and 3)
Run Code Online (Sandbox Code Playgroud)

等等...

我们希望能够进行查询,我们说所有用户都获得条件1,在这种情况下,即使他们还有其他条件,它也会返回用户1和3.

非常感谢任何见解,只在C#中使用Flags,而不是直接在Sql Server中使用.

Jam*_* L. 25

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
Run Code Online (Sandbox Code Playgroud)

此查询返回以下数据集:

id          username             flags
----------- -------------------- -----------
1           User1                6
3           User3                14
Run Code Online (Sandbox Code Playgroud)


bri*_*ian 18

虽然James建议的按位运算符可以工作,但它在关系数据库中不会非常高效,特别是当您尝试扩展到数百万条记录时.原因是where子句中的函数不是sargable(它们阻止索引查找).

我要做的是创建一个表,其中包含标志和条件的所有可能组合,这将启用索引查找条件.

填充FlagConditions.我用了一个(tinyint).如果您需要更多Flags,您应该能够扩展这种方法:

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;
Run Code Online (Sandbox Code Playgroud)

现在,您可以在需要有效搜索枚举按位条件时使用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;
Run Code Online (Sandbox Code Playgroud)

返回:

Username   Flag
---------- ----
User1      6
User3      14
Run Code Online (Sandbox Code Playgroud)

你的DBA会感谢你走这条面向集合的路线.


sae*_*ehp 6

我有几乎相同的问题,可以提出这样的解决方案:

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
Run Code Online (Sandbox Code Playgroud)

结果如下:

value       type
----------  ------------------------------------
14          Type1, Type2, Type3
12          Type2, Type3
14          Type1, Type2, Type3
Run Code Online (Sandbox Code Playgroud)