[英]dynamic interval creation in SQL
我有以下问题,我想用transact-sql解决。 我有这样的东西
Start | End | Item
1 | 5 | A
3 | 8 | B
我想创建类似
Start | End | Item-Combination
1 | 2 | A
3 | 5 | A-B
6 | 8 | B
对于Item-Combination串联,我已经考虑过使用FOR XML语句。 但是为了创建不同的新间隔,我真的不知道该如何处理。 任何的想法?
谢谢。
我对某些计算机使用情况数据有一个非常相似的问题。 我有指示登录/注销时间的会话数据。 我想找到最需要的时间(一周中的每天几小时),即最多用户登录的时间。我最终使用哈希表解决了客户端的问题。 对于每个会话,我将为该会话处于活动状态的每一天/小时中对应于星期几和一天中的一天的特定位置增加存储桶。 检查完所有会话后,哈希表值显示一周中每一天每一小时内的登录次数。
我认为您可以做类似的事情,跟踪每个开始/结束值看到的每个项目。 然后,您可以通过折叠具有相同项目组合的相邻条目来重建表。
而且,不,我也想不到一种解决SQL问题的方法。
这是一个相当典型的范围查找问题,并引发了串联。不确定以下内容是否完全合适,但这是一个起点。 (通常最好避免使用游标,除非在少数情况下它们比基于集合的解决方案要快,所以在讨厌游标之前,请注意我故意在这里使用游标,因为这对我来说就像游标友好问题-我通常会避免使用它们。)
因此,如果我创建这样的数据:
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书籍中有一些很好的例子。
这将完全模拟并解决上述问题:
-- 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
非常感谢所有答案,目前我已经找到了一种解决方法。 因为我正在处理一个数据仓库,并且有一个“时间”维度,所以我可以使用“在f.start_date和end_date之间的t.date上的内部联接DimTime t”样式对Time维度进行一些联接。
从性能的角度来看,这不是很好,但似乎对我有用。
我将尝试onupdatecascade实施,以了解哪种方法更适合我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.