2009-08-01 3 views
4

У меня есть следующая проблема, которую я хотел бы решить с помощью transact-sql. у меня есть что-то вроде этогосоздание динамического интервала в SQL

Start | End | Item 
    1 | 5 | A 
    3 | 8 | B 

и я хочу, чтобы создать что-то вроде

Start | End | Item-Combination 
    1 | 2 | A 
    3 | 5 | A-B 
    6 | 8 | B 

для элемента-Combination конкатенации я уже думал об использовании для заявления XML. Но для того, чтобы создать разные новые интервалы ... Я действительно не знаю, как подойти к нему. Есть идеи?

Спасибо.

+0

Какую версию SQL Server вы используете? – Sung

+0

Сколько предметов может пересекаться? (т. е. это всегда 2, a и b, или это может быть любое число?) – onupdatecascade

+0

SQL Server 2008, а количество элементов, наложенных на них, может быть любым – river0

ответ

1

У меня была очень похожая проблема с некоторыми данными об использовании компьютера. У меня были данные сеанса, указывающие время входа/выхода. Я хотел найти время (час дня в день недели), который наиболее востребован, то есть часы, когда большинство пользователей вошли в систему. Я решил решить проблему на стороне клиента, используя хеш-таблицы. Для каждого сеанса я увеличивал бы ведро в определенном месте, соответствующем дню недели и часу дня для каждого дня/часа, для которого сеанс был активным. После изучения всех сеансов значения хэш-таблицы показывают количество логинов в течение каждого часа для каждого дня недели.

Я думаю, вы могли бы сделать что-то подобное, отслеживая каждый элемент, видимый для каждого значения начала/конца. Затем вы можете восстановить таблицу, свернув смежные записи, имеющие одну и ту же комбинацию элементов.

И, нет, я тоже не мог придумать способ решить мою проблему с SQL.

1

Это довольно типичная проблема с поиском дальности с включенной конкатенацией. Не уверен, что следующее соответствует точно, но это отправная точка. (Курсоры обычно лучше избегать, за исключением небольшого набора случаев, когда они быстрее, чем решения на основе набора, поэтому перед тем, как нападающие курсора пойдут на меня, обратите внимание, что я использую курсор здесь специально, потому что это пахнет мне, как удобный для курсора . проблема - я обычно их избежать)

Так что, если я создаю такие данные:

 
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 

Тогда я могу относиться к проблеме так: каждый «Пуск» и каждый «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.

0

Это будет точно эмулирует и решает указанную проблему:


-- prepare problem, it can have many rows with overlapping ranges 
declare @range table 
(
    Item char(1) primary key, 
    [Start] int, 
    [End] int 
) 
insert @range select 'A', 1, 5 
insert @range select 'B', 3, 8 

-- unroll the ranges into helper table 
declare @usage table 
(
    Item char(1), 
    Number int 
) 

declare 
    @Start int, 
    @End int, 
    @Item char(1) 

declare table_cur cursor local forward_only read_only for 
    select [Start], [End], Item from @range 
open table_cur 
fetch next from table_cur into @Start, @End, @Item 
while @@fetch_status = 0 
begin 
    with 
    Num(Pos) as -- generate numbers used 
    (
     select cast(@Start as int) 
     union all 
     select cast(Pos + 1 as int) from Num where Pos < @End 
    ) 
    insert 
     @usage 
    select 
     @Item, 
     Pos 
    from 
     Num 
    option (maxrecursion 0) -- just in case more than 100 

    fetch next from table_cur into @Start, @End, @Item 
end 
close table_cur 
deallocate table_cur 

-- compile overlaps 
; 
with 
overlaps as 
(
    select 
     Number, 
     (
      select 
       Item + '-' 
      from 
       @usage as i 
      where 
       o.Number = i.Number 
      for xml path('') 
     ) 
     as Items 
    from 
     @usage as o 
    group by 
     Number 
) 
select 
    min(Number) as [Start], 
    max(Number) as [End], 
    left(Items, len(Items) - 1) as Items -- beautify 
from 
    overlaps 
group by 
    Items 
0

Большое спасибо за ответы на все вопросы, на данный момент я нашел способ сделать это. SInce Я имею дело с хранилищем данных, и у меня есть измерение времени, я мог бы сделать несколько объединений с измерением времени в стиле «внутреннее объединение DimTime t на t.date между f.start_date и end_date».

Это не очень хорошо с точки зрения производительности, но, похоже, это работает для меня.

Я попробую выполнить onupdatecascade, чтобы увидеть, что подходит для меня лучше.

+0

Ehm, небольшая отметка была бы оценена, спасибо! :) –