31

如何在 SQL Server 中使用 C# 中的枚举等标志的处理?


例如,如何返回属于列表或条件一部分的用户列表,如下所示:

ConditionAlpha = 2
ConditionBeta  = 4
ConditionGamma = 8

...

然后会有一些用户对他们有这些条件,如下所示:

User1: 6 (conditions Alpha and Beta)
User2: 4 (condition Beta)
User3: 14 (conditions Alpha, Beta and Gamma)

...

我们希望能够进行查询,我们说让所有用户都具有第一个条件 Alpha,在这种情况下,即使他们还有其他条件,它也会返回用户 1 和 3。

4

5 回答 5

33

用于检查 SQL 中是否设置标志的位运算符是&. 该WHERE子句需要计算为一个BOOLEAN表达式,如下所示:

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

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

declare @ConditionOne int = 2

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

declare @ConditionTwo int = 4

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

declare @ConditionThree int = 8

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

drop table #temp

这些查询返回以下结果集:

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

id          username             flags
----------- -------------------- -----------
1           User1                6
2           User2                4
3           User3                14

id          username             flags
----------- -------------------- -----------
3           User3                14
于 2012-11-30T12:52:58.497 回答
24

虽然 James 建议的按位运算符会起作用,但它在关系数据库中的性能并不高,尤其是当您尝试扩展到数百万条记录时。原因是 where 子句中的函数是不可搜索的(它们阻止了索引查找)。

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

填充标志条件。我用了一个(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

您的 DBA 会感谢您走这条面向设置的路线。

于 2012-11-30T15:46:03.903 回答
7

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

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
于 2015-11-16T14:47:30.277 回答
1

C# 枚举:CopEntry = 1 << 17

SQL 服务器:case when (Features & power(2, 17)) = 0 then 0 else 1 end as COPEntry

于 2018-07-02T14:36:22.773 回答
0

与纯 T-SQL相同因此它不仅适用于 SSMS 或其他地方

DROP TABLE IF EXISTS flag_conditions
CREATE TABLE flag_conditions
(
    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

WITH x AS (SELECT n FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) v(n))
INSERT #Flags
    (DummyColumn)
SELECT NULL
FROM (
         SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n
         FROM x ones,
              x tens,
              x hundreds,
              x thousands
     ) t
WHERE n <= 256;

SELECT *
FROM #Flags;

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

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

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

DROP TABLE #Flags;
DROP TABLE #Conditions;

SELECT *
FROM flag_conditions;
于 2022-02-16T13:39:27.040 回答