Это довольно типичная проблема определения дальности с добавленной конкатенацией. Не уверен, что следующее точно соответствует, но это отправная точка. (Курсоров обычно лучше избегать, за исключением небольшого набора случаев, когда они быстрее, чем решения на основе множеств, поэтому, прежде чем ненавистники курсора попадут на меня, обратите внимание, что я специально использую курсор здесь, потому что это пахнет для меня как дружественный к курсору проблема - я обычно их избегаю.)
Итак, если я создам данные, подобные этим:
CREATE TABLE [dbo].[sourceValues](
[Start] [int] NOT NULL,
[End] [int] NOT NULL,
[Item] [varchar](100) NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[sourceValues] WITH CHECK ADD CONSTRAINT [End_after_Start] CHECK (([End]>[Start]))
GO
ALTER TABLE [dbo].[sourceValues] CHECK CONSTRAINT [End_after_Start]
GO
declare @i int; set @i = 0;
declare @start int;
declare @end int;
declare @item varchar(100);
while @i < 1000
begin
set @start = ABS( CHECKSUM( newid () ) % 100 ) + 1 ; -- "random" int
set @end = @start + ( ABS( CHECKSUM( newid () ) % 10 ) ) + 2; -- bigger random int
set @item = char( ( ABS( CHECKSUM( newid() ) ) % 5 ) + 65 ); -- random letter A-E
print @start; print @end; print @item;
insert into sourceValues( Start, [End], Item) values ( @start , @end, @item );
set @i += 1;
end
Тогда я могу рассматривать проблему следующим образом: каждое значение «Начало» И каждое значение «Конец» представляет собой изменение в коллекции текущих элементов, либо добавление одного, либо удаление одного в определенный момент времени. В приведенном ниже коде я называю это «событие», что означает «Добавить» или «Удалить». Каждое начало или конец похожи на время, поэтому я использую термин «тик». Если я создаю коллекцию всех событий, упорядоченных по времени события (начало и конец), я могу выполнять итерацию по ней, сохраняя подсчет результатов в таблице в памяти всех элементов, которые находятся в игре. Каждый раз, когда изменяется значение тика, я делаю снимок этого подсчета:
declare @tick int;
declare @lastTick int;
declare @event varchar(100);
declare @item varchar(100);
declare @concatList varchar(max);
declare @currentItemsList table ( Item varchar(100) );
create table #result ( Start int, [End] int, Items varchar(max) );
declare eventsCursor CURSOR FAST_FORWARD for
select tick, [event], item from (
select start as tick, 'Add' as [event], item from sourceValues as adds
union all
select [end] as tick, 'Remove' as [event], item from sourceValues as removes
) as [events]
order by tick
set @lastTick = 1
open eventsCursor
fetch next from eventsCursor into @tick, @event, @item
while @@FETCH_STATUS = 0
BEGIN
if @tick != @lastTick
begin
set @concatList = ''
select @concatList = @concatlist + case when len( @concatlist ) > 0 then '-' else '' end + Item
from @currentItemsList
insert into #result ( Start, [End], Items ) values ( @lastTick, @tick, @concatList )
end
if @event = 'Add' insert into @currentItemsList ( Item ) values ( @item );
else if @event = 'Remove' delete top ( 1 ) from @currentItemsList where Item = @item;
set @lastTick = @tick;
fetch next from eventsCursor into @tick, @event, @item;
END
close eventsCursor
deallocate eventsCursor
select * from #result order by start
drop table #result
Использование курсора для этого особого случая позволяет всего один «проход» через данные, как проблема с промежуточными итогами. Ицик Бен-Ган привел несколько замечательных примеров в своих книгах по SQL 2005.