2013-07-25 2 views
2

Я застрял на SQL-запросе. Я использую SQL Server.SQL-запрос для расчета дней работы за месяц

Данная таблица содержит задания с датой начала и окончания. Эти рабочие места могут охватывать дни или месяцы. Мне нужно получить общее количество дней, которые были обработаны каждый месяц для всех рабочих мест, которые пересекали эти месяцы.

Работа

----------------------------------- 
JobId | Start | End | DayRate | 
----------------------------------- 
1  | 1.1.13 | 2.2.13 | 2500 | 
2  | 5.1.13 | 5.2.13 | 2000 | 
3  | 3.3.13 | 2.4.13 | 3000 | 

Результаты мне нужно являются:

Month | Days 
-------------- 
Jan | 57 
Feb | 7 
Mar | 28 
Apr | 2 

Любая идея, как я бы право такой запрос?

Я также хотел бы разработать СУММ для каждого месяца на основе умножения дневного курса на количество дней, отработанных для каждой работы, как бы добавить это к результатам?

Благодаря

+2

ли все дни в период работы или может быть отдых? –

+0

Все дни работают, нет необходимости учитывать праздники. – JCoder23

ответ

2

Вы можете использовать рекурсивное CTE, чтобы извлечь все дни от начала до конца для каждого JobID, а затем только группы по месяцам (и году я думаю).

;WITH CTE_TotalDays AS 
(
    SELECT [Start] AS DT, JobID FROM dbo.Jobs 
    UNION ALL 
    SELECT DATEADD(DD,1,c.DT), c.JobID FROM CTE_TotalDays c 
    WHERE c.DT < (SELECT [End] FROM Jobs j2 WHERE j2.JobId = c.JobID) 
) 
SELECT 
    MONTH(DT) AS [Month] 
    ,YEAR(DT) AS [Year] 
    ,COUNT(*) AS [Days] 
FROM CTE_TotalDays 
GROUP BY MONTH(DT),YEAR(DT) 
OPTION (MAXRECURSION 0) 

SQLFiddle DEMO

PS: Есть 58 дней в январе в вашем примере, а не 57;)

+0

Я получаю сообщение об ошибке, когда я запускаю его против моих данных: максимальная рекурсия 100 исчерпана до завершения инструкции. – JCoder23

+0

ОК, я добавил параметр MAXRECURSIONS и установлен в 0, он отлично работает. – JCoder23

+0

@ JCoder23, Oh, 100 - максимальная рекурсия по умолчанию для CTE (для предотвращения бесконечных циклов). Вы можете установить собственное значение с добавлением 'OPTION (MAXRECURSION n)' в конце запроса - 0 для бесконечного. Обновлен запрос и демо. –

0

Вы можете сделать это, используя следующий подход:

/* Your table with periods */ 
declare @table table(JobId int, Start date, [End] date, DayRate money) 

INSERT INTO @table (JobId , Start, [End], DayRate) 
VALUES 
(1, '20130101','20130202', 2500), 
(2,'20130105','20130205', 2000), 
(3,'20130303','20130402' , 3000) 

/* create table where stored all possible dates 
    if this code are supposed to be executed often you can create 
    table with dates ones to avoid overhead of filling it */ 
declare @dates table(d date) 

declare @d date='20000101' 
WHILE @d<'20500101' 
    BEGIN 
     INSERT INTO @dates (d) VALUES (@d) 
     SET @d=DATEADD(DAY,1,@d) 
    END; 

/* and at last get desired output */ 

SELECT YEAR(d.d) [YEAR], DATENAME(month,d.d) [MONTH], COUNT(*) [Days] 
FROM @dates d 
    CROSS JOIN @table t 
WHERE d.d BETWEEN t.Start AND t.[End] 
GROUP BY YEAR(d.d), DATENAME(month,d.d) 
0

Этот только 1 рекурсивный вызов вместо 1 для каждой строки. Я полагаю, что это будет лучше, чем выбранный ответ, когда у вас будет большой объем данных.

declare @t table(JobId int, Start date, [End] date, DayRate int) 
insert @t values 
(1,'2013-01-01','2013-02-02', 2500),(2,'2013-01-05','2013-02-05', 2000),(3,'2013-03-03', '2013-04-02',3000) 

;WITH a AS 
(

SELECT min(Start) s, max([End]) e 
FROM @t 
), b AS 
(
SELECT s, e from a 
UNION ALL 
SELECT dateadd(day, 1, s), e 
FROM b WHERE s <> e 
) 
SELECT 
    MONTH(b.s) AS [Month] 
    ,YEAR(b.s) AS [Year] 
    ,COUNT(*) AS [Days] 
    ,SUM(DayRate) MonthDayRate 
FROM b 
join @t t 
on b.s between t.Start and t.[End] 
GROUP BY MONTH(b.s),YEAR(b.s) 
OPTION (MAXRECURSION 0) 

Результат:

Month Year Days MonthDayRate 
1  2013 58  131500 
2  2013 7  15000 
3  2013 29  87000 
4  2013 2  6000 
Смежные вопросы