31

C# の列挙型などのフラグの処理を SQL Server でどのように使用できますか?


たとえば、次のようなリストまたは条件の一部であるユーザーのリストを返す方法は次のとおりです。

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 句の関数がサージ可能ではない(インデックス シークを防止する) ためです。

私がすることは、フラグと条件の可能なすべての組み合わせを含むテーブルを作成することです。これにより、条件でのインデックスシークが可能になります。

FlagConditions を入力します。シングル(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 に答える