Пересечения диапазонов дат - PullRequest
0 голосов
/ 16 марта 2010

MS Sql 2008:

У меня есть 3 таблицы: счетчики, трансформаторы (Ti) и трансформаторы напряжения (Tu)

ParentId  MeterId  BegDate       EndDate  
10      100      '20050101'    '20060101'

ParentId  TiId     BegDate       EndDate  
10      210      '20050201'    '20050501'
10      220      '20050801'    '20051001'

ParentId  TuId   BegDate       EndDate  
10      300      '20050801'    '20050901'

, где формат даты - ггггMMdd (год-месяц-день)

Есть ли способ получить пересечение периодов и вернуть таблицу следующим образом?

ParentId  BegDate     EndDate     MeterId   TiId   TuId    
10      '20050101'  '20050201'  100       null   null 
10      '20050201'  '20050501'  100       210    null 
10      '20050501'  '20050801'  100       null   null 
10      '20050801'  '20050901'  100       220    300 
10      '20050901'  '20051001'  100       220    null
10      '20051001'  '20060101'  100       null   null 

Вот сценарий создания таблицы:

--meters
declare @meters table 
(ParentId int,
MeterId int,
BegDate smalldatetime,
EndDate smalldatetime
)
insert @meters
select 10, 100, '20050101', '20060101'

--transformers
declare @ti table 
(ParentId int,
TiId int,
BegDate smalldatetime,
EndDate smalldatetime
)
insert @ti
select 10,   210,    '20050201',     '20050501'
union all 
select 10,   220,    '20050801',     '20051001'



--voltage transformers
declare @tu table 
(ParentId int,
TuId int,
BegDate smalldatetime,
EndDate smalldatetime
)

insert @tu
select 10,   300,    '20050801',     '20050901'

Ответы [ 2 ]

3 голосов
/ 16 марта 2010

Это должно работать:

Здесь происходит то, что я использую CTE (общее табличное выражение), чтобы отделить запрос на основе UNION, который дает нам все точки даты, которые можно использовать для построения интервалов.

Как только это будет сделано, мы можем использовать ROW_NUMBER (), чтобы дать нам смежные пары дат для использования в качестве интервалов, и как только мы получим их, просто присоединиться к вашим данным просто.

Надеюсь, это поможет!


;WITH dates (dval) AS
(
    SELECT DISTINCT begdate AS dval
    FROM @meters m
    UNION
    SELECT DISTINCT enddate AS dval
    FROM @meters m
    UNION
    SELECT DISTINCT begdate AS dval
    FROM @ti ti
    UNION
    SELECT DISTINCT enddate AS dval
    FROM @ti ti
    UNION
    SELECT DISTINCT begdate AS dval
    FROM @tu tu
    UNION
    SELECT DISTINCT enddate AS dval
    FROM @tu tu
)
SELECT m.Parentid, d1.dval AS begdate, d2.dval AS enddate, m.meterid, ti.tiid, tu.tuid
FROM
(
    SELECT dval, ROW_NUMBER() OVER (ORDER BY dval ASC) AS rnum
    FROM dates
) d1
    INNER JOIN 
    (
        SELECT dval, ROW_NUMBER() OVER (ORDER BY dval ASC) AS rnum
        FROM dates
    ) d2 ON d1.rnum+1 = d2.rnum
    LEFT JOIN @meters m ON m.begdate <= d1.dval AND m.enddate >= d2.dval
    LEFT JOIN @ti ti ON ti.begdate <= d1.dval AND ti.enddate >= d2.dval
    LEFT JOIN @tu tu ON tu.begdate <= d1.dval AND tu.enddate >= d2.dval
0 голосов
/ 17 марта 2010

mwigdahl,
Большое спасибо! Я только что изменил ваше решение для использования с ParentId в случае, когда таблица метров будет содержать строки с разными parentId, например:

ParentId  MeterId  BegDate       EndDate  
10      100      '20050101'    '20060101'
20      110      '20050201'    '20050701'

Вот сценарий

    --meters
DECLARE @Meters
TABLE   (
        ParentId    INTEGER,
        MeterId     INTEGER,
        BegDate     SMALLDATETIME,
        EndDate     SMALLDATETIME
        )

--transformers
DECLARE @TI
TABLE   (
        ParentId    INTEGER,
        TiId        INTEGER,
        BegDate     SMALLDATETIME,
        EndDate     SMALLDATETIME
        )

--voltage transformers
DECLARE @TU
TABLE   (
        ParentId    INTEGER,
        TuId        INTEGER,
        BegDate     SMALLDATETIME,
        EndDate     SMALLDATETIME
        )

INSERT  @Meters (ParentId, MeterId, BegDate, EndDate)
SELECT  10, 100, '20050101', '20060101'
UNION   ALL 
SELECT  20, 110, '20050201', '20050701'

INSERT  @TI (ParentId, TiId, BegDate, EndDate)
SELECT  10, 210, '20050201', '20050501'
UNION   ALL 
SELECT  10, 220, '20050801', '20051001'
UNION   ALL 
SELECT  20, 230, '20050101', '20050301'
UNION   ALL 
SELECT  20, 240, '20050501', '20051001'



INSERT  @TU (ParentId, TuId, BegDate, EndDate)
SELECT  10, 300, '20050801', '20050901'
UNION   ALL 
SELECT  20, 310, '20050101', '20050601'


;with dM (ParentId, MeterId) as
(
select distinct ParentId, MeterId from @meters
),
dates (ParentId, meterid, dval) AS
(
    SELECT ParentId, meterid, begdate AS dval
    FROM @meters m
    UNION
    SELECT ParentId, meterid, enddate AS dval
    FROM @meters m
    UNION
    SELECT ti.ParentId, meterid, begdate AS dval
    FROM @ti ti
    join dM dm on ti.ParentId = dm.ParentId
    UNION
    SELECT ti.ParentId, meterid, enddate AS dval
    FROM @ti ti
    join dM dm on ti.ParentId = dm.ParentId
    UNION
    SELECT tu.ParentId, meterid, begdate AS dval
    FROM @tu tu
    join dM dm on tu.ParentId = dm.ParentId
    UNION
    SELECT tu.ParentId, meterid, enddate AS dval
    FROM @tu tu
    join dM dm on tu.ParentId = dm.ParentId
)
select m.ParentId, d1.dval AS begdate, d2.dval AS enddate, m.MeterId, TiId, TuId
FROM
(
    SELECT ParentId, meterid, dval, ROW_NUMBER() OVER (ORDER BY ParentId asc, meterid ASC, dval ASC) AS rnum
    FROM dates
) d1
    INNER JOIN 
    (
        SELECT ParentId, meterid, dval, ROW_NUMBER() OVER (ORDER BY ParentId asc, meterid ASC, dval ASC) AS rnum
        FROM dates
    ) d2 ON d1.ParentId = d2.ParentId and d1.meterid = d2.meterid and d1.rnum+1 = d2.rnum
    LEFT JOIN @meters m ON m.ParentId = d1.ParentId and m.ParentId = d2.ParentId and m.meterid = d1.meterid and m.meterid = d2.meterid and m.begdate <= d1.dval AND m.enddate >= d2.dval
    LEFT JOIN @ti ti ON ti.ParentId = d1.ParentId and ti.ParentId = d2.ParentId and ti.begdate <= d1.dval AND ti.enddate >= d2.dval
    LEFT JOIN @tu tu ON tu.ParentId = d1.ParentId and tu.ParentId = d2.ParentId and tu.begdate <= d1.dval AND tu.enddate >= d2.dval
    where not (m.ParentId is null) and not (m.meterid is null)
    order by d1.ParentId, d1.MeterId, d1.dval, d2.dval
;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...