【问题标题】:SQL Server Bitwise Processing like C# Enum FlagsSQL Server 按位处理,如 C# 枚举标志
【发布时间】:2012-11-18 16:22:39
【问题描述】:

如何在 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。

【问题讨论】:

  • 将此信息存储在多对多表中会更“类似于 SQL”。因此,您会将行 (1,1),(1,2),(2,2),(3,1),(3,2),(3,3) 存储在单独的表中。这将使查询更加自然,并提供索引机会。

标签: sql sql-server bitwise-operators


【解决方案1】:

用于检查 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

【讨论】:

    【解决方案2】:

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

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

    填充标志条件。我用了一个(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 会感谢您走这条面向设置的路线。

    【讨论】:

    • 当它包含的 SQL 甚至没有解析(“go 256”)时,我不明白这个答案如何有 20 个赞成票。即使删除了 256,它也没有按照答案所说的那样做。
    • @IanKemp 这将在 SSMS 或 sqmcmd 中工作。 GO 不是 T-SQL 语句,而是批处理分隔符。它被 SSMS 等 sql 工具识别。它的语法为 GO [count],其中 count 是运行前一批的次数。所以在这种情况下,它运行插入 256 次。文档
    • 优雅但如果您需要过滤两个或更多条件怎么办?
    • 你为每个条件做一个额外的连接?
    • 这是一个非常优雅的解决方案。我冒昧地更新了生成 256 行的纯 TSQL 方法的答案,因为并非所有客户端都支持 GO n
    【解决方案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
    

    【讨论】:

      【解决方案4】:

      C# 枚举:CopEntry = 1 &lt;&lt; 17

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

      【讨论】:

        猜你喜欢
        • 2023-03-28
        • 1970-01-01
        • 2018-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多