我有以下问题,我想用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语句。但是为了创造不同的新间隔...我真的不知道如何处理它。有什么想法吗?
感谢。
答案 0 :(得分:1)
我的一些计算机使用数据问题非常类似。我有会话数据表明登录/注销时间。我想找到最需要的时间(一周中每天的每小时),即大多数用户登录的小时数。我最终使用哈希表解决了客户端问题。对于每个会话,我会针对与会话处于活动状态的每天/每小时的星期几和小时相对应的特定位置递增桶。检查所有会话后,哈希表值显示每周每天的登录次数。
我认为你可以做类似的事情,跟踪每个开始/结束值的每个项目。然后,您可以通过折叠具有相同项目组合的相邻条目来重建表格。
而且,不,我想不出用SQL解决我的问题的方法。
答案 1 :(得分: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书籍中有一些很好的例子。
答案 2 :(得分: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
答案 3 :(得分:0)
非常感谢所有答案,目前我找到了一种方法。我正在处理一个数据仓库,我有一个时间维度,我可以使用“内部联接DimTime t on t.date f.start_date和end_date”之类的时间维度进行一些连接。
从性能的角度来看,这不是很好,但它似乎对我有用。
我将尝试onupdatecascade实现,看看哪种更适合我。