2011-01-21 10 views
5

En el servidor sql tengo una tabla con fechas de inicio y finalización para las publicaciones de trabajo. Dado un mes y un año por el usuario, necesito encontrar la cantidad de días entre la fecha de inicio y la fecha de finalización que corresponden al año/mes determinado para esa publicación.Seleccione el número de días que un registro está presente dentro de un determinado año/mes

lo tanto, si la fecha de contabilización de inicio es 2010/11/15 y 2010/12/05 Fecha Final es Luego salida debe ser:

November 16 days 
December 5 days 
Total  21 days 

He estado golpeando mi cabeza contra la pared con este uno y estoy recién salido de ideas.

Respuesta

2

Esta es probablemente la respuesta más concisa.

declare @start datetime, @end datetime 
select @start = '20101115', @end = '20101205' 

select datename(month,@start+number), count(*) 
from master..spt_values 
where type='P' 
    and number between 0 and datediff(d,@start,@end) 
group by datename(month,@start+number), convert(char(6),@start+number,112) 
order by convert(char(6),@start+number,112) 

va a trabajar para distancias de hasta 2.048 días (7-8 años), pero puede extenderse por más tiempo si es necesario (sólo bajo petición - que la voluntad se ve más complicado).

La única razón para la parte convert(char es hacer que noviembre aparezca antes de diciembre, y también antes de enero del próximo año.

+0

¡Guau, algo de revelación! Por supuesto, usar la tabla del sistema ahorra la necesidad de crear y poblar la nuestra. Pero lo principal para mí aquí es la forma en que elegiste contar los días. ¡Increíble! He encontrado una versión similar a la que otros publicaron, pero ahora me da vergüenza mostrarla: ¡tan trivial! –

+0

@Andriy - sería bueno si votaste la respuesta :) – RichardTheKiwi

+0

@cyberwiki: Muy cierto, lo siento. Pensé que lo había votado. Muy amable de su parte recordarme mis propias intenciones. :) –

2

que desea utilizar DateDiff

DateDiff("d",[StartDate],[EndDate])

la d anterior contará días.

+0

Sé cómo usar datediff. Si tuviera dos fechas para comparar, entonces lo usaría. El problema es más complejo e implica dos intervalos de fechas superpuestos. – ChrisOPeterson

+0

@ChirsOPeterson: ¿Acabo de volver a leer su pregunta, pero no vi qué superposición usted menciona? Oh, espera, la salida que tienes, ¿es así como te gustaría? Si es así, OMG Ponies u otros gurús de sitios tendrán que responder a eso porque no estoy seguro en este punto. – VoodooChild

+0

La salida específica que anoté no es tan importante. Solo necesito obtener los valores. La superposición se produce a partir del intervalo entre la Fecha de inicio del trabajo y la Fecha de finalización, y luego cada mes en que la Fecha de inicio y la Fecha de finalización pueden ser total o parcialmente parte de. – ChrisOPeterson

1

EDIT: Otra solución de menor importancia o dos ...

Editado para dar la respuesta completa ...

declare @user_start_date datetime 
set @user_start_date = '1/1/2011' 
declare @user_end_date datetime 
set @user_end_date = '1/10/2011' 
declare @job_start_date datetime 
set @job_start_date = '1/2/2011' 
declare @job_end_date datetime 
set @job_end_date = '1/11/2011' 

declare @nextStartDate datetime; 
set @nextStartDate = str(datepart(mm, @user_start_date)) + '/1/' + str(datepart(yyyy, @user_start_date)) 
declare @nextEndDate datetime; 
set @nextEndDate = dateadd(dd,-1,dateadd(mm,1,@nextStartDate)) 

create table #monthYears(startDate datetime, endDate datetime) 
while (@nextStartDate < @user_end_date) begin 
    insert into #monthYears values(@nextStartDate, @nextEndDate) 
    set @nextStartDate = dateadd(mm,1,@nextStartDate) 
    set @nextEndDate = dateadd(dd,-1,dateadd(mm,1,@nextStartDate)) 
end 

-- Print Months 
select [month], [year], case when dayCount < 0 then 0 else dayCount end from (
select month(startDate) month, year(startDate) year , datediff(dd, 
    case when startDate > @job_start_date then startDate else @job_start_date end, 
    case when endDate < @job_end_date then endDate else @job_end_date end) dayCount 
from #monthYears) temp 

select datediff(dd, 
    case when @user_start_date > @job_start_date then @user_start_date else @job_start_date end, 
    case when @user_end_date < @job_end_date then @user_end_date else @job_end_date end) 
1

Esto es un poco difícil en SQL pero esto creará una tabla con el mes (en número entero) y la cantidad de días.

Lo dejo a usted para convertir el entero al mes y añadir el total de

SET NOCOUNT on 

Declare @StartDate datetime 
Declare @EndDate datetime 
Declare @StartDateNormalized datetime 
Declare @EndDateNormalized datetime 


SET @StartDate = '2010/11/15' 
SET @EndDate = '2011/2/05' 

declare @result table (month int, days int) 


--Normalize the Inputs 

SET @StartDateNormalized = cast(Month(@startDate) as varchar) + '/1/' + cast(year(@startDate) as varchar) 
SET @EndDateNormalized = cast(Month(@EndDate) as varchar) + '/1/' + cast(year(@EndDate) as varchar) 

insert into @result 
values 
( MONTH(@StartDateNormalized), 
    DateDiff(Day, @StartDate, DateAdd(month, 1, @StartDateNormalized)) 
) 

SET @StartDateNormalized = DateAdd(month, 1, @StartDateNormalized) 

WHILE (@StartDateNormalized < @EndDateNormalized) 
BEGIN 

insert into @result 
values 
( MONTH(@StartDateNormalized), 
    DateDiff(Day, @StartDateNormalized, DateAdd(month, 1, @StartDateNormalized)) 
) 

    SET @StartDateNormalized = DateAdd(month, 1, @StartDateNormalized) 
END 

insert into @result 
values 
( MONTH(@EndDateNormalized), 
    DateDiff(Day, @EndDateNormalized, @EndDate ) + 1 
) 


select * from @result 
+0

Wow este se ve muy interesante. ¡Gracias! – ChrisOPeterson

1
DECLARE 
    @StartDate datetime, 
    @EndDate datetime; 
SET @StartDate = '20101115'; 
SET @EndDate = '20101205'; 

WITH Mos AS (
    SELECT 
     Number, 
     DateAdd(Month, Number, @StartDate - Day(@StartDate) + 1) MoDate 
    FROM master.dbo.spt_values 
    WHERE 
     Type = 'P' 
     AND Number <= DateDiff(Month, @StartDate, @EndDate) 
), Dys AS (
    SELECT 
     MoDate, 
     DateDiff(
     Day, 
     CASE WHEN Number = 0 THEN @StartDate ELSE MoDate END, 
     CASE WHEN Number = DateDiff(Month, @StartDate, @EndDate) THEN @EndDate ELSE DateAdd(Month, 1, MoDate) - 1 END 
    ) + 1 Cnt 
    FROM Mos 
) 
SELECT 
    Year(MoDate) Yr, 
    Coalesce(DateName(Month, MoDate), 'Total') Mo, 
    Convert(varchar(11), Sum(Cnt)) + ' day' + CASE WHEN Sum(Cnt) = 1 THEN '' ELSE 's' END Descr 
FROM Dys 
GROUP BY MoDate 
WITH ROLLUP 
ORDER BY Grouping(MoDate), MoDate; 

Y aquí es una versión de mesa en caso de que quería hacerlo con muchos a la vez:

CREATE TABLE AccountDates (
    AccountCode varchar(10) NOT NULL CONSTRAINT PK_AccountDates PRIMARY KEY CLUSTERED, 
    StartDate datetime, 
    EndDate datetime 
); 

INSERT AccountDates VALUES ('BLINKEN', '20101115', '20101205'); 
INSERT AccountDates VALUES ('KRAM', '20101027', '20110118'); 
INSERT AccountDates VALUES ('NUVU', '20101207', '20101207'); 

WITH Mos AS (
    SELECT 
     AccountCode, 
     D.StartDate, 
     D.EndDate, 
     Number, 
     DateAdd(Month, Number, D.StartDate - Day(D.StartDate) + 1) MoDate 
    FROM 
     AccountDates D 
     INNER JOIN master.dbo.spt_values V ON V.Number <= DateDiff(Month, D.StartDate, D.EndDate) 
    WHERE 
     V.Type = 'P' 
), Dys AS (
    SELECT 
     AccountCode, 
     MoDate, 
     DateDiff(
     Day, 
     CASE WHEN Number = 0 THEN StartDate ELSE MoDate END, 
     CASE WHEN Number = DateDiff(Month, StartDate, EndDate) THEN EndDate ELSE DateAdd(Month, 1, MoDate) - 1 END 
    ) + 1 Cnt 
    FROM Mos 
) 
SELECT 
    AccountCode, 
    Year(MoDate) Yr, 
    Coalesce(DateName(Month, MoDate), 'Total') Mo, 
    Convert(varchar(11), Sum(Cnt)) + ' day' + CASE WHEN Sum(Cnt) = 1 THEN '' ELSE 's' END Descr 
FROM Dys 
GROUP BY AccountCode, MoDate 
WITH ROLLUP 
HAVING Grouping(AccountCode) = 0 
ORDER BY 
    AccountCode, 
    Grouping(MoDate), 
    MoDate; 

He simplificado un poco las cosas desde mi consulta original.

+0

Esta es una respuesta realmente genial también. Gracias por enviar. – ChrisOPeterson

+0

Por favor, por favor, no me importan los puntos, pero si baja la votación, ¿tomaría solo 10 segundos para darme una idea de lo que no fue satisfactorio con mi respuesta? Siempre me esfuerzo por mejorar y amo la corrección, incluso la corrección mayor, cuando se da con tacto. – ErikE

Cuestiones relacionadas