私はC#の列挙型でフラグを使用し、すべてが正常ですが、次のシナリオで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が返されます。
洞察をいただければ幸いです。C#でのみフラグを使用し、SQLServerでは直接使用しません。
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
Jamesが提案したビット演算子は機能しますが、リレーショナルデータベースでは、特に数百万のレコードにスケーリングしようとすると、パフォーマンスが低下します。その理由は、where句の関数はsargableではない(インデックスシークを妨げる)ためです。
私がすることは、フラグと条件のすべての可能な組み合わせを含むテーブルを作成することです。これにより、条件でのインデックスシークが可能になります。
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は、このセット指向のルートを進んでくれてありがとう。
私はほぼ同じ問題を抱えていて、そのような解決策を思い付くことができました:
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
C#列挙型:_CopEntry = 1 << 17
_
SQL Server:case when (Features & power(2, 17)) = 0 then 0 else 1 end as COPEntry