日期范围交叉点

时间:2010-03-16 12:53:31

标签: sql-server tsql sql-server-2008

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'

其中日期格式为yyyyMMdd(年 - 月 - 日)

有没有办法让句点相交并像这样返回表格?

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 个答案:

答案 0 :(得分:3)

这应该有效:

这里发生的是我使用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

答案 1 :(得分:0)

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
;
相关问题