2011-10-03 5 views
5

que tengo una tabla de registros de honorarios (f_fee_item) de la siguiente manera:Encuentra lagunas en el tiempo no cubiertos por los registros con fecha de inicio y fecha de finalización

Fee_Item_ID int 
Fee_Basis_ID int 
Start_Date  date 
End_Date  date 

(columnas irrelevantes eliminados)

asumir que los registros de el mismo Fee_Basis_ID no se superpondrá.

Necesito encontrar el Start_Date y el End_Date de cada espacio en los registros de tarifas para cada Fee_Basis_ID entre un @Query_Start_Date suministrado y @Query_End_Date. Necesito estos datos para calcular las acumulaciones de tarifas para todos los períodos en los que no se han cobrado tarifas.

También necesito la consulta para devolver un registro si no hay ningún registro de tarifas para un determinado Fee_Basis_ID (Fee_Basis_ID es una clave externa para D_Fee_Basis.Fee_Basis_ID si eso ayuda).

Por ejemplo:

@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 

resultados requeridos:

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 

He estado probando diferentes autouniones durante días tratando de que funcione pero sin suerte.

Por favor ayuda !!

+0

cuántos registros tiene 'F_Fee_Item 'tabla? –

+0

Se espera que permanezca por debajo de 5 cifras durante la vida útil del sistema – Aphillippe

Respuesta

2

Aquí es una solución:

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) 

Enlace de resultado: http://data.stackexchange.com/stackoverflow/q/114193/

+0

Perfecto. Gracias por la ayuda. Realmente necesito aprender CTE correctamente. – 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: otra solución CTE

Cuestiones relacionadas