2010-03-16 60 views
0

MS SQL 2008:日期範圍交叉

我有3個表:米,變壓器(Ti)和電壓互感器(塗)

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' 

,其中日期格式爲年月日(年 - 月 - 日)

有沒有什麼辦法讓段相交,並返回表這樣的嗎?

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' 
+0

你能解釋一下如何你拿出'BegDate'和您的樣品結果'EndDate'設置? – 2010-03-16 14:06:26

回答

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 
; 
3

這應該工作:

這是怎麼回事,是我用一個CTE(公共表表達式),以分解出聯盟 - 該查詢爲我們提供了所有日期點以用於構建間隔。

一旦做到這一點,我們可以使用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

+1,做好@mwigdahl! – 2010-03-16 17:27:00