Следующий подход обходится без like
и использует битовые маски.
CharIndex
используется для определения наличия в строке конкретной буквы. Возвращает либо одну позицию символа, либо ноль. Sign
используется, чтобы сложить все положительные значения до 1
при прохождении 0
через. При использовании подходящих множителей (1, 2, 4, 8, 16) совпадения объединяются в битовую маску, причем младший значащий бит (LSB) равен понедельнику, ....
Побитовая арифметика может использоваться для битовых масок. Побитовое И вернет все биты, которые установлены (1
) в обоих аргументах. Если нет общих установленных битов, то результат будет нулевым.
Процесс можно изменить, чтобы преобразовать битовую маску в строку букв дня.
Для повышения производительности битовые маски могут храниться в постоянном вычисляемом столбце. Обратите внимание, что индексация вряд ли будет очень полезна.
-- Sample data.
declare @Meetings as Table ( Id Int Identity, MetDays VarChar(5) );
insert into @Meetings ( MetDays ) values
( 'MWF' ), ( 'TH' ), ( 'M' ), ( 'T' ), ( 'WHF' );
select * from @Meetings;
-- Play with the data.
declare @BusyDays as VarChar(5) = 'MWF'; -- I'm busy these days.
with
BusyDays as ( -- Build a bitmask of the days that I'm busy.
select @BusyDays as BusyDays,
Sign( CharIndex( 'M', @BusyDays ) ) +
Sign( CharIndex( 'T', @BusyDays ) ) * 2 +
Sign( CharIndex( 'W', @BusyDays ) ) * 4 +
Sign( CharIndex( 'H', @BusyDays ) ) * 8 +
Sign( CharIndex( 'F', @BusyDays ) ) * 16 as BusyDaysBitMask ),
MetDays as ( -- Build a bitmask for the days each meeting occurs.
select MetDays,
Sign( CharIndex( 'M', MetDays ) ) +
Sign( CharIndex( 'T', MetDays ) ) * 2 +
Sign( CharIndex( 'W', MetDays ) ) * 4 +
Sign( CharIndex( 'H', MetDays ) ) * 8 +
Sign( CharIndex( 'F', MetDays ) ) * 16 as MetDaysBitMask
from @Meetings )
select MD.MetDays, MD.MetDaysBitMask, BD.BusyDays, BD.BusyDaysBitMask,
-- Bitwise AND of day bitmasks. Zero means no days in common.
MD.MetDaysBitMask & BD.BusyDaysBitMask as CollisionDaysBitMask,
CD.CollisionDays
from BusyDays as BD cross join
MetDays as MD cross apply
( select -- Convert the collision bitmask back to a set of day letters.
case when MD.MetDaysBitMask & BD.BusyDaysBitMask & 1 != 0 then 'M' else '' end +
case when MD.MetDaysBitMask & BD.BusyDaysBitMask & 2 != 0 then 'T' else '' end +
case when MD.MetDaysBitMask & BD.BusyDaysBitMask & 4 != 0 then 'W' else '' end +
case when MD.MetDaysBitMask & BD.BusyDaysBitMask & 8 != 0 then 'H' else '' end +
case when MD.MetDaysBitMask & BD.BusyDaysBitMask & 16 != 0 then 'F' else '' end as
CollisionDays ) CD;
Для сравнения встреч по общим дням:
with
MetDays as ( -- Build a bitmask for the days each meeting occurs.
select MetDays,
Sign( CharIndex( 'M', MetDays ) ) +
Sign( CharIndex( 'T', MetDays ) ) * 2 +
Sign( CharIndex( 'W', MetDays ) ) * 4 +
Sign( CharIndex( 'H', MetDays ) ) * 8 +
Sign( CharIndex( 'F', MetDays ) ) * 16 as MetDaysBitMask
from @Meetings )
select MDL.MetDays as 'MetDays Left', MDL.MetDaysBitMask as 'MetDaysBitMask Left',
MDR.MetDays as 'MetDays Right', MDR.MetDaysBitMask as 'MetDaysBitMask Right',
-- Bitwise AND of day bitmasks. Zero means no days in common.
MDL.MetDaysBitMask & MDR.MetDaysBitMask as CollisionDaysBitMask,
CD.CollisionDays
from MetDays as MDL cross join
MetDays as MDR cross apply
( select -- Convert the collision bitmask back to a set of day letters.
case when MDL.MetDaysBitMask & MDR.MetDaysBitMask & 1 != 0 then 'M' else '' end +
case when MDL.MetDaysBitMask & MDR.MetDaysBitMask & 2 != 0 then 'T' else '' end +
case when MDL.MetDaysBitMask & MDR.MetDaysBitMask & 4 != 0 then 'W' else '' end +
case when MDL.MetDaysBitMask & MDR.MetDaysBitMask & 8 != 0 then 'H' else '' end +
case when MDL.MetDaysBitMask & MDR.MetDaysBitMask & 16 != 0 then 'F' else '' end as
CollisionDays ) CD;
Если буквы всегда будут в одном и том же порядке, тогда можно использовать небольшую справочную таблицу для сопоставления всех 32 комбинаций дней недели с соответствующими значениями битовой маски. Несколько большую таблицу можно использовать, если порядок букв не гарантирован. Это заменит все строковые манипуляции и арифметику поиском таблицы в любом направлении.