2009-08-01 6 views
4

다음과 같은 문제가 있는데, 그 문제는 transact-sql로 해결하고 싶습니다. 나는이SQL에서의 동적 간격 생성

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

같은 것을하고 난 이미 XML 문에 대한을 사용하여 생각 항목-조합 연결에 대한

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

같은 것을 만들려고합니다. 그러나 다른 새로운 간격을 만들기 위해 ... 나는 정말로 그것에 접근하는 방법을 모른다. 어떤 생각?

감사합니다.

+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 

그런 다음 나는이 같은 문제를 처리 할 수 ​​있습니다 : 각각의 "시작"을 각각 "끝"을 값의 변화를 나타냅니다 현재 항목의 컬렉션에서 특정 시간에 하나 추가하거나 제거합니다. 아래의 코드에서이 개념을 "이벤트"라는 별칭으로 추가 또는 제거합니다. 각 시작 또는 끝은 시간과 같으므로 용어 "틱"을 사용합니다. 이벤트 시간 (시작 및 종료)별로 정렬 된 모든 이벤트의 컬렉션을 만들면 재생중인 모든 항목의 메모리 내장 테이블에서 실행 집계를 유지하면서 반복 할 수 있습니다. 눈금 값이 변경, 내가 그 집계의 스냅 샷을 할 때마다 :

 
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 

이 특별한 경우에 커서를 사용하여 단 하나의 누계 문제와 같은 데이터를 통해 "통과"할 수 있습니다. Itzik Ben-Gan은 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

고마워, 내가 그 일을하는 방법을 발견 한 순간을. 내가 데이터웨어 하우스를 다루고 있고, Time 차원이 있는데, "f.start_date와 end_date 사이의 t.date에 내부 연결 DimTime t"스타일로 Time 차원과 조인을 할 수 있습니다.

성능면에서별로 좋지는 않지만 저에게는 효과적 인 것으로 보입니다.

나에게 맞는 방법을 확인하기 위해 onupdatecascade 구현을 시도해 보겠습니다.

+0

흠, 약간의 노치가 가장 감사하게 생각합니다. 감사합니다! :) –