这个很难解决。
我有一个包含日期范围的表,每个日期范围都有一个优先级。最高优先级意味着此日期范围是最重要的。
或者在SQL
中create table #ranges (Start int, Finish int, Priority int)
insert #ranges values (1 , 10, 0)
insert #ranges values (2 , 5 , 1)
insert #ranges values (3 , 4 , 2)
insert #ranges values (1 , 5 , 0)
insert #ranges values (200028, 308731, 0)
Start Finish Priority
----------- ----------- -----------
1 10 0
2 5 1
3 4 2
1 5 0
200028 308731 0
我想在此表上运行一系列SQL查询,这将导致表没有重叠范围,它将采用优先级较高的范围而不是较低的范围。根据需要拆分范围,并摆脱重复范围。它允许存在差距。
所以结果应该是:
Start Finish Priority
----------- ----------- -----------
1 2 0
2 3 1
3 4 2
4 5 1
5 10 0
200028 308731 0
有人想关注SQL吗?我也希望它尽可能高效。
答案 0 :(得分:4)
这是大多数方式,可能的改进是加入相同优先级的相邻范围。它充满了酷炫的诡计。
select Start, cast(null as int) as Finish, cast(null as int) as Priority
into #processed
from #ranges
union
select Finish, NULL, NULL
from #ranges
update p
set Finish = (
select min(p1.Start)
from #processed p1
where p1.Start > p.Start
)
from #processed p
create clustered index idxStart on #processed(Start, Finish, Priority)
create index idxFinish on #processed(Finish, Start, Priority)
update p
set Priority =
(
select max(r.Priority)
from #ranges r
where
(
(r.Start <= p.Start and r.Finish > p.Start) or
(r.Start >= p.Start and r.Start < p.Finish)
)
)
from #processed p
delete from #processed
where Priority is null
select * from #processed
答案 1 :(得分:0)
我对你最终想要的东西感到有点困惑。这是否只是简单地设置一组日期,其中一个范围一直持续到下一个范围开始(在这种情况下,你真的不需要完成日期,对吗?)
或者一个范围可以完成,并且有时会出现间隙,直到下一个有时开始?
如果明确设置了Start和Finish范围,那么我倾向于保留两者,但是在重叠期间有逻辑应用更高的优先级。我怀疑如果日期开始调整,你最终需要回滚剃光范围,原来的设置就会消失。
你永远无法解释“它是如何做到的”。
您是否只想要一个每个日期都有一行的表,包括其优先级值?那么当你有一个新规则时,你可以碰撞新规则会胜过的日期吗?
我做了一个医疗办公室调度应用程序,一旦开始工作/休假等。具有范围类型数据的请求(加上默认的工作周模板。)一旦我想出将活动计划信息存储为用户/日期/时间范围记录,事情就会变得更加容易。 YMMV。
答案 2 :(得分:0)
这是让你入门的东西。如果您使用日历表,它会很有用:
CREATE TABLE dbo.Calendar
(
dt SMALLDATETIME NOT NULL
PRIMARY KEY CLUSTERED
)
GO
SET NOCOUNT ON
DECLARE @dt SMALLDATETIME
SET @dt = '20000101'
WHILE @dt < '20200101'
BEGIN
INSERT dbo.Calendar(dt) SELECT @dt
SET @dt = @dt + 1
END
GO
设置问题的代码:
create table #ranges (Start DateTime NOT NULL, Finish DateTime NOT NULL, Priority int NOT NULL)
create table #processed (dt DateTime NOT NULL, Priority int NOT NULL)
ALTER TABLE #ranges ADD PRIMARY KEY (Start,Finish, Priority)
ALTER TABLE #processed ADD PRIMARY KEY (dt)
declare @day0 datetime,
@day1 datetime,
@day2 datetime,
@day3 datetime,
@day4 datetime,
@day5 datetime
select @day0 = '2000-01-01',
@day1 = @day0 + 1,
@day2 = @day1 + 1,
@day3 = @day2 + 1,
@day4 = @day3 + 1,
@day5 = @day4 + 1
insert #ranges values (@day0, @day5, 0)
insert #ranges values (@day1, @day4, 1)
insert #ranges values (@day2, @day3, 2)
insert #ranges values (@day1, @day4, 0)
实际解决方案:
DECLARE @start datetime, @finish datetime, @priority int
WHILE 1=1 BEGIN
SELECT TOP 1 @start = start, @finish = finish, @priority = priority
FROM #ranges
ORDER BY priority DESC, start, finish
IF @@ROWCOUNT = 0
BREAK
INSERT INTO #processed (dt, priority)
SELECT dt, @priority FROM calendar
WHERE dt BETWEEN @start and @finish
AND NOT EXISTS (SELECT * FROM #processed WHERE dt = calendar.dt)
DELETE FROM #ranges WHERE @start=start AND @finish=finish AND @priority=priority
END
结果: SELECT * FROM #processed
dt Priority
----------------------- -----------
2000-01-01 00:00:00.000 0
2000-01-02 00:00:00.000 1
2000-01-03 00:00:00.000 2
2000-01-04 00:00:00.000 2
2000-01-05 00:00:00.000 1
2000-01-06 00:00:00.000 0
解决方案的格式不完全相同,但想法就在那里。
答案 3 :(得分:0)
这可以在1个SQL中完成(我首先在Oracle中使用lag和lead进行查询,但由于MSSQL不支持这些函数,我使用row_number重写了查询。我不确定结果是否符合MSSQL ,但它应该非常接近):
with x as (
select rdate rdate
, row_number() over (order by rdate) rn
from (
select start rdate
from ranges
union
select finish rdate
from ranges
)
)
select d.begin
, d.end
, max(r.priority)
from (
select begin.rdate begin
, end.rdate end
from x begin
, x end
where begin.rn = end.rn - 1
) d
, ranges r
where r.start <= d.begin
and r.finish >= d.end
and d.begin <> d.end
group by d.begin
, d.end
order by 1, 2
我首先制作了一张包含所有日期的表格(x)。然后我通过将x与自身连接并将以下两行连接到桶中。在此之后,我将所有可能的优先级与结果联系起来。通过取max(优先级),我得到了请求的结果。