Было совершенно очевидно использовать для этого побитовые операторы, так как это, в основном, то, что перечисление flags использует внутри. Я нашел способ выполнить sh это:
Редактировать: предыдущий запрос был неправильным, и я думаю, что вопрос не совсем ясен. Сначала я предоставлю некоторый фон;
У нас есть объект, который может иметь любое из 20+ состояний. Чтобы предотвратить создание более 20 булевых столбцов в нашей таблице, мы храним целочисленное значение нашего помеченного перечисления.
Теперь, чтобы использовать эти данные в нашей системе шаблонов, нам нужен эффективный способ запроса объектов с помощью их состояние.
В следующем примере я сделаю запрос для всех объектов, помеченных как 'State_2'
-- Set up the table and fill it up with some example data
create table #ObjectsWithMultipleStates (Flag int, ObjectValue nvarchar(255))
insert into #ObjectsWithMultipleStates
values
(1, 'Object_1'),(2, 'Object_2'),(3, 'Object_3'),(4, 'Object_4'),(5, 'Object_5'),
(6, 'Object_6'),(7, 'Object_7'),(8, 'Object_8'),(9, 'Object_9'),(10, 'Object_10'),
(11, 'Object_11'),(12, 'Object_12'),(13, 'Object_13'),(14, 'Object_14'),(15, 'Object_15'),
(16, 'Object_16'),(17, 'Object_17'),(18, 'Object_18'),(19, 'Object_19'),(20, 'Object_20')
-- Example flag enum, which these values relate to
create table #States (Id int, [Name] nvarchar(255))
insert into #States values (1, 'State_1'),(2, 'State_2'),(4, 'State_3'),(8, 'State_4'),(16, 'State_5')
-- For this example, we'll get the enum's int value by its name
declare @FlagValue int = (select Id from #States where [Name] = 'State_2')
-- Returns 2, 3, 6, 7, 10, 11, 14, 15, 18, 19 (which seems about right)
select * from #ObjectsWithMultipleStates
where Flag|@FlagValue = Flag
Как отметил TomTom, это не позволяет эффективно использовать индексы, что делает этот запрос довольно медленный.
Решением этой проблемы может быть выполнение побитового запроса всех возможных опций в памяти, поэтому мы можем эффективно использовать индексы:
select * from #ObjectsWithMultipleStates where Flag in (
-- This returns all possible flag combinations (would be wrapped in a UDF in reality)
select Val
from(
SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n[Val]
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)
WHERE ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n BETWEEN 1 AND POWER(2, (select count(*) from #States)) -1
) possibleValues
where Val|@FlagValue = Val)
Но это имеет довольно много накладных расходов.
Есть ли более эффективный способ справиться с этим?
РЕДАКТИРОВАТЬ # 2: Ответ Venkataraman R заставил меня понять, что сохранение фактического значения флага - глупая идея, и мы никогда не будем возможность запросить этот эффективный запрос.
Чтобы решить эту проблему, нам понадобится таблица отношений, которая связывает каждое состояние, в котором находится объект, с объектом.
-- Note that I've removed the flags column, and added an Id from this example
create table #ObjectsWithMultipleStates (Id int, ObjectValue nvarchar(255))
insert into #ObjectsWithMultipleStates
values
(1, 'Object_1'),(2, 'Object_2'),(3, 'Object_3'),(4, 'Object_4'),(5, 'Object_5'),
(6, 'Object_6'),(7, 'Object_7'),(8, 'Object_8'),(9, 'Object_9'),(10, 'Object_10'),
(11, 'Object_11'),(12, 'Object_12'),(13, 'Object_13'),(14, 'Object_14'),(15, 'Object_15'),
(16, 'Object_16'),(17, 'Object_17'),(18, 'Object_18'),(19, 'Object_19'),(20, 'Object_20')
-- Example flag enum, which these values relate to
create table #States (Id int, [Name] nvarchar(255))
insert into #States values (1, 'State_1'),(2, 'State_2'),(4, 'State_3'),(8, 'State_4'),(16, 'State_5')
-- Example relationship table
create table #ObjectState(ObjectId int, StateId int)
insert into #ObjectState values
(1, 1), (2, 2), (3, 1), (3, 2) -- Etc.
declare @FlagValue int = (select Id from #States where [Name] = 'State_2')
-- Finally, we can perform a decent query
select * from #ObjectsWithMultipleStates where Id in (
select ObjectId from #ObjectState where StateId = @FlagValue
)
Я думаю, что это самое эффективное, мы сможем получить его.