2011-10-03 12 views
5

Mam tabeli rekordów opłatą (f_fee_item) w następujący sposób:Znajdź luki w czasie nieobjęte rekordów z datą rozpoczęcia i data zakończenia

Fee_Item_ID int 
Fee_Basis_ID int 
Start_Date  date 
End_Date  date 

(w dowolnej kolumny usuwane)

Załóżmy, że rekordy ten sam identyfikator Fee_Basis_ID nie będzie się nakładał.

Muszę znaleźć datę początkową i datę końcową każdej przerwy w rekordach opłat dla każdego Fee_Basis_ID między dostarczonymi @Query_Start_Date i @Query_End_Date. Potrzebuję tych danych, aby obliczyć naliczone opłaty za wszystkie okresy, w których opłaty nie zostały naliczone.

Potrzebuję również zapytania, aby zwrócić rekord, jeśli nie ma żadnych rekordów opłat dla danego Fee_Basis_ID (Fee_Basis_ID jest kluczem obcym do D_Fee_Basis.Fee_Basis_ID, jeśli to pomaga).

Na przykład:

@Query_Start_Date = '2011-01-01' 
@Query_Start_Date = '2011-09-30' 

D_Fee_Basis:

F_Fee_Item 
1 
2 
3 

F_Fee_Item:

Fee_Item_ID Fee_Basis_ID Start_Date End_Date 
1   1    2011-01-01 2011-03-31 
2   1    2011-04-01 2011-06-30 
3   2    2011-01-01 2011-03-31 
4   2    2011-05-01 2011-06-30 

Wymagane Wyniki:

Fee_Basis_ID Start_Date End_Date 
1    2011-07-01 2011-09-30 
2    2011-04-01 2011-04-30 
2    2011-07-01 2011-09-30 
3    2011-01-01 2011-09-30 

mam pszczoła próbuje różnych self-dołącza do dni próbujących dostać pracy, ale bez powodzenia.

Proszę o pomoc!

+0

ile rekordów ma 'F_Fee_Item 'stół? –

+0

Oczekuje się, że pozostanie poniżej 5 cyfr na czas życia systemu. – Aphillippe

Odpowiedz

2

Oto rozwiązanie:

declare @Query_Start_Date date= '2011-01-01' 
declare @Query_End_Date date = '2011-09-30' 

declare @D_Fee_Basis table(F_Fee_Item int) 
insert @D_Fee_Basis values(1) 
insert @D_Fee_Basis values(2) 
insert @D_Fee_Basis values(3) 

declare @F_Fee_Item table(Fee_Item_ID int, Fee_Basis_ID int,Start_Date date,End_Date date) 
insert @F_Fee_Item values(1,1,'2011-01-01','2011-03-31') 
insert @F_Fee_Item values(2,1,'2011-04-01','2011-06-30') 
insert @F_Fee_Item values(3,2,'2011-01-01','2011-03-31') 
insert @F_Fee_Item values(4,2,'2011-05-01','2011-06-30') 

;with a as 
(-- find all days between Start_Date and End_Date 
select @Query_Start_Date d 
union all 
select dateadd(day, 1, d) 
from a 
where d < @Query_end_Date 
), b as 
(--find all unused days 
select a.d, F_Fee_Item Fee 
from a, @D_Fee_Basis Fee 
where not exists(select 1 from @F_Fee_Item where a.d between Start_Date and End_Date and Fee.F_Fee_Item = Fee_Basis_ID) 
), 
c as 
(--find all start dates 
select d, Fee, rn = row_number() over (order by fee, d) from b 
where not exists (select 1 from b b2 where dateadd(day,1, b2.d) = b.d and b2.Fee= b.Fee) 
), 
e as 
(--find all end dates 
select d, Fee, rn = row_number() over (order by fee, d) from b 
where not exists (select 1 from b b2 where dateadd(day,-1, b2.d) = b.d and b2.Fee= b.Fee) 
) 
--join start dates with end dates 
select c.Fee Fee_Basis_ID, c.d Start_Date, e.d End_Date from c join e on c.Fee = e.Fee and c.rn = e.rn 
option (maxrecursion 0) 

link dla wyniku: http://data.stackexchange.com/stackoverflow/q/114193/

+0

Doskonały. Dzięki za pomoc. Naprawdę muszę się dobrze uczyć CTE. – Aphillippe

1

YACTES:

CREATE TABLE dbo.TestData 
(
    Fee_Item_ID  INT PRIMARY KEY, 
    Fee_Basis_ID INT NOT NULL, 
    [Start_Date] DATE NOT NULL, 
    [End_Date]  DATE NOT NULL 
); 
CREATE UNIQUE INDEX [email protected] 
ON  dbo.TestData(Fee_Basis_ID, [Start_Date]) 
INCLUDE (End_Date); 

INSERT dbo.TestData 
VALUES 
(1,1,'2011-02-01','2011-03-31'), 
(2,1,'2011-04-01','2011-05-31'), 
(3,2,'2011-01-01','2011-03-31'), 
(4,2,'2011-05-01','2011-06-30'), 
(5,2,'2011-11-01','2011-11-30'), 
(6,3,'2011-05-01','2011-06-30'); 

CREATE TABLE dbo.TestData2(Fee_Basis_ID INT PRIMARY KEY); 
INSERT dbo.TestData2 VALUES (1),(2),(3); 

DECLARE @Query_Start_Date DATE = '2011-01-01' 
    ,@Query_End_Date DATE = '2011-09-30'; 

;WITH FeeRank 
AS 
(
    SELECT a.Fee_Basis_ID 
      ,a.[Start_Date] 
      ,a.End_Date 
      ,ROW_NUMBER() OVER(PARTITION BY a.Fee_Basis_ID ORDER BY a.[Start_Date]) GroupRowNum 
      ,COUNT(*)  OVER(PARTITION BY a.Fee_Basis_ID) GroupCount 
    FROM dbo.TestData a 
    WHERE @Query_Start_Date <= a.[Start_Date] 
    AND  a.[End_Date] <= @Query_End_Date 
), Scan 
AS 
(
    SELECT a.Fee_Basis_ID, @Query_Start_Date [Start_Date], DATEADD(DAY,-1,a.[Start_Date]) End_Date 
    FROM FeeRank a 
    WHERE a.GroupRowNum = 1 
    AND  @Query_Start_Date <> a.[Start_Date] 

    UNION ALL 

    SELECT a.Fee_Basis_ID, DATEADD(DAY,1,a.End_Date) b, @Query_End_Date c 
    FROM FeeRank a 
    WHERE a.End_Date <> @Query_End_Date AND a.GroupRowNum = a.GroupCount 

    UNION ALL 

    SELECT crt.Fee_Basis_ID, DATEADD(DAY,1,prev.End_Date), DATEADD(DAY,-1,crt.[Start_Date]) 
    FROM FeeRank prev 
    INNER JOIN FeeRank crt ON prev.Fee_Basis_ID = crt.Fee_Basis_ID 
    AND  prev.GroupRowNum + 1 = crt.GroupRowNum 
    WHERE DATEDIFF(DAY,prev.[End_Date],crt.[Start_Date]) <> 1 
) 
SELECT cte.Fee_Basis_ID, cte.[Start_Date], cte.End_Date 
FROM Scan cte 
UNION ALL 
SELECT b.Fee_Basis_ID, NULL, NULL 
FROM dbo.TestData2 b 
WHERE b.Fee_Basis_ID NOT IN (SELECT c.Fee_Basis_ID FROM dbo.TestData c) 
ORDER BY Fee_Basis_ID; 

DROP TABLE dbo.TestData; 
DROP TABLE dbo.TestData2 

YACTES: Yet Another CTE Rozwiązanie

Powiązane problemy