EAg*_*rre 2 sql date sql-server-2008
我有一个棘手的问题。我有一个社会保障缴款表如下(SQL Fiddle)
IdPersona fecha_ingreso fecha_egreso
5690180 01/01/1987 30/11/2012
5690180 01/01/2010 30/11/2012
5690180 11/06/2012 15/11/2012
5690180 12/04/2012 25/04/2012
5690180 16/03/2012 30/03/2012
5690180 18/06/2011 15/10/2011
5690180 20/12/2012 20/01/2013
5690180 21/11/2012 15/12/2012
Run Code Online (Sandbox Code Playgroud)
每行代表一个人的工作。个人可能同时拥有不止一份工作。我需要计算每个人在特定时期内做出的贡献天数,例如在去年,问题是我必须只计算不同的贡献天数,而不是对一个人在同一天多次计算。我怎样才能做到这一点?
概述可能的解决方案来计算天数但不是唯一的 1-定义日期范围,一个时间窗口,用于计算人们所做的贡献。2-对于每个响应者ID,我必须计算在此期间输入窗口有多少天?
DECLARE @FchRef DATETIME
SET @FchRef ='2011-05-01'
--Fhc referencia de comienzo del programa a partir de cual calcular aportes
DECLARE @PeriRef SMALLINT
SET @PeriRef =24
--Periodo a partir de la Fch ref para contar aportes
DECLARE @FchCotDesde DATETIME
SET @FchCotDesde=Dateadd(MONTH, -24, @FchRef)
--Fch a partir de la cual calcular aportes
SELECT ut.documento 'CI_UT',
sum(DATEDIFF(DAY, CASE
WHEN CONVERT(DATETIME, act.fecha_ingreso, 103) < @FchCotDesde THEN @FchCotDesde
ELSE CONVERT(DATETIME, act.fecha_ingreso, 103)
END, CASE
WHEN fecha_egreso = '' THEN @FchRef
ELSE CONVERT(DATETIME, act.fecha_egreso, 103)
END)) 'DiasContados'
FROM dbo.[UT2011_12] ut
LEFT JOIN dbo.DatosPersonalesyDomicilios dat
ON cast(cast(ut.documento AS DECIMAL(12, 0))AS VARCHAR) = dat.nro_documento
LEFT JOIN dbo.Actividades act
ON act.pers_identificador = dat.pers_identificador
AND ( CONVERT(DATETIME, act.fecha_egreso, 103) = ''
OR CONVERT(DATETIME, act.fecha_egreso, 103) >= @FchCotDesde )
-- La Fch de egreso es vacia ó con Fch posterior a fch ref
AND CONVERT(DATETIME, act.fecha_ingreso, 103) <= @FchRef
--La Fch de inicio de act es anterior a la FchRef
WHERE ut.UT_2011_Inscriptos = 1
OR ut.UT_2012_Inscriptos = 1
GROUP BY ut.documento
Run Code Online (Sandbox Code Playgroud)
此解决方案的问题在于,如果日期范围(入口和出口)重叠,它会起作用。我想到了这个问题的解决方案:1 - 创建一个包含每个人的游标 2 - 我创建一个循环,循环通过日复一日的窗口期,如果你抛出一个贡献和 0 但是,对于每个人个人。我提出的解决方案将如何极其复杂。你能想到更好的解决方案吗?
使用此脚本,您可以创建表
create table #aux(
Idpersona int
,FechaIngreso datetime
,FechaEgreso datetime
)
insert into #aux values(5690180,'1987/01/01','2012/11/30')
insert into #aux values(5690180,'2010/01/01','2012/11/30')
insert into #aux values(5690180,'2012/06/11','2012/11/15')
insert into #aux values(5690180,'2012/04/12','2012/04/25')
insert into #aux values(5690180,'2012/03/16','2012/03/30')
insert into #aux values(5690180,'2011/06/18','2011/10/15')
insert into #aux values(5690180,'2012/12/20','2013/01/20')
insert into #aux values(5690180,'2012/11/21','2012/12/15')
select * from #aux
Run Code Online (Sandbox Code Playgroud)
我看到问题的好方法是用一张图形
111111111111111111111111111000000000000000000000000000000000000000000000000000000000
|----A1----|
|----A2----|
|----A3----|
00000111111111111111110000001111111111111111111111111000000000000000000000000000000
|------B1-------|
|----------B2-----------|
Run Code Online (Sandbox Code Playgroud)
可能最简单的方法是创建一个辅助日期表。
CREATE TABLE Dates(D DATE PRIMARY KEY)
/*
Load dates between 1990-01-01 and 2049-12-31*/
INSERT INTO Dates
SELECT TOP (21915) DATEADD(DAY,ROW_NUMBER() OVER (ORDER BY (SELECT 1)), '19891231')
AS N
FROM master..spt_values v1,
master..spt_values v2;
Run Code Online (Sandbox Code Playgroud)
然后你可以做
DECLARE @FchRef DATE = '20110501';
DECLARE @FchCotDesde DATE = DATEADD(MONTH, -24, @FchRef);
SELECT [IdPersona],
COUNT(DISTINCT Dates.D) AS Contributions
FROM Contributions
JOIN Dates ON Dates.D BETWEEN fecha_ingreso AND fecha_egreso
WHERE Dates.D BETWEEN DATEADD(MONTH, -24, @FchRef) AND @FchRef
GROUP BY IdPersona
Run Code Online (Sandbox Code Playgroud)