2011-01-21 15 views
5

Na serwerze sql mam tabelę z datami rozpoczęcia i zakończenia dla ofert pracy. Biorąc pod uwagę miesiąc i rok przez użytkownika, muszę znaleźć liczbę dni między datą początkową a końcową, które przypadają w danym roku/miesiącu dla tego postu.Wybierz liczbę dni, w których rekord jest obecny w danym roku/miesiącu.

Więc jeśli wpis jest 15.11.2010 Data rozpoczęcia i data zakończenia to 05.12.2010 Następnie wyjście powinno być:

November 16 days 
December 5 days 
Total  21 days 

byłem bić głową o ścianę z tym jeden i jestem świeży z pomysłów.

Odpowiedz

2

Jest to prawdopodobnie najbardziej zwięzła odpowiedź.

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) 

Będzie pracować dla zakresów do 2048 dni (7-8 lat), ale może być przedłużony na dłużej, jeśli trzeba (na życzenie tylko - że wola wygląda bardziej skomplikowane).

Jedynym powodem dla części convert(char jest sprawienie, aby listopad pojawił się przed grudniem, a także przed stycznia następnego roku.

+0

Wow, niektóre rewelacje! Oczywiście korzystanie z tabeli systemowej powoduje konieczność tworzenia i zapełniania własnych. Ale najważniejsze dla mnie jest sposób, w jaki wybierasz dni. Niesamowite! Wymyśliłem wersję podobną do tego, co napisali inni, ale teraz wstydzę się jej pokazać: tak trywialnie! –

+0

@Andriy - byłoby miło, gdybyś wznowił odpowiedź :) – RichardTheKiwi

+0

@cyberwiki: Bardzo, przepraszam. Myślałem, że go przegłosowałem. Bardzo miło z twojej strony, że przypominasz mi o moich własnych zamiarach. :) –

2

Chcesz użyć DateDiff

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

d powyżej będzie liczyć dni.

+0

Wiem, jak korzystać z datediff. Gdybym miał dwie daty do porównania, użyłbym go. Problem jest bardziej złożony i obejmuje dwa nakładające się okresy czasu. – ChrisOPeterson

+0

@ChirsOPeterson: Po prostu ponownie przeczytałem twoje pytanie, ale nie widziałem, co się pokrywa, o czym mówisz? Och, czekaj, wyjście, które masz, jest takie, jak by ci się to podobało? Jeśli to jest wtedy Kucyki OMG lub inni guru strony będą musieli odpowiedzieć na to, ponieważ nie jestem pewien w tym momencie. – VoodooChild

+0

Dane wyjściowe, które zapisałem, nie są tak ważne. Po prostu potrzebuję uzyskać wartości. Nakładanie się odbywa się w przedziale między datą rozpoczęcia pracy a datą zakończenia, a następnie w każdym miesiącu data rozpoczęcia i zakończenia może w całości lub w części być częścią. – ChrisOPeterson

1

EDIT: Jeszcze drobne poprawki lub dwa ...

Edited by dać pełną odpowiedź ...

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

To jest trochę trudne w SQL ale stworzy to tabelę z miesiącem (w liczbie całkowitej) i liczbą dni.

Zostawię ci go przekonwertować liczbę całkowitą do miesiąca i dodać całkowita

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, ten wygląda naprawdę interesująco. Dzięki! – 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; 

A oto wersja tabeli w przypadku, gdy chciałem zrobić to z wielu na raz:

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; 

Uproszyłem trochę rzeczy z mojego pierwotnego zapytania.

+0

To również bardzo fajna odpowiedź. Dziękujemy za przesłanie zgłoszenia. – ChrisOPeterson

+0

Proszę, proszę, proszę: nie dbam o punkty, ale jeśli zgodziłbyś się, czy poświęciłbyś 10 sekund, aby dać mi znać, co jest niezadowalające w mojej odpowiedzi? Zawsze staram się poprawić i pokochać korektę, nawet poważną korektę, gdy taktownie podana. – ErikE

Powiązane problemy