Очистка приоритетных перекрывающихся диапазонов в SQL Server - PullRequest
1 голос
/ 10 февраля 2009

Это неприятно сложно решить.

У меня есть таблица, содержащая диапазоны дат, каждый диапазон дат имеет приоритет. Самый высокий приоритет означает, что этот диапазон дат является самым важным.

Или в 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? Я также хотел бы, чтобы это было максимально эффективно.

Ответы [ 4 ]

4 голосов
/ 10 февраля 2009

Это большая часть пути, возможное улучшение - объединение смежных диапазонов с одинаковым приоритетом. Он полон крутой хитрости.

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
0 голосов
/ 10 февраля 2009

Это можно сделать в 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 с собой и взяв 2 следующих ряда. После этого я связал все возможные приоритеты с результатом. Взяв максимум (приоритет), я получаю запрошенный результат.

0 голосов
/ 10 февраля 2009

Вот кое-что, с чего можно начать. Это полезно, если вы используете таблицу календаря:

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 # обработано

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

Решение не в том же формате, но идея есть.

0 голосов
/ 10 февраля 2009

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

Или диапазон может закончиться, и есть промежуток, пока иногда не начинается следующий?

Если диапазон Start и Finish задан явно, то я склонен оставить оба, но иметь логику для применения более высокого приоритета во время перекрытия. Я подозреваю, что если даты начнут корректироваться, вам, в конечном счете, придется откатить диапазон, который был выбрит, и исходная настройка исчезнет.

И вы никогда не сможете объяснить, «как это получилось».

Хотите ли вы просто таблицу со строкой для каждой даты, включая значение приоритета? Затем, когда у вас есть новое правило, вы можете изменить даты, которые будут превзойдены новым правилом?

Однажды я запустил приложение для планирования медицинского кабинета, которое начиналось с работы / отпуска / и т. Д. запросы с данными типа диапазона (плюс шаблон рабочей недели по умолчанию). Как только я решил сохранить информацию об активном расписании как записи пользователя / даты / временного диапазона, все стало намного проще. YMMV.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...