У меня есть следующая таблица, которая имеет отсутствие сотрудников:Суммирования и группировка количества записей в месяце
RecordId EmpID ActivityCode DateFrom DateTo
---------------------------------------------------------------
666542 1511 AB 29/01/2011 02/02/2011
666986 1511 AB 11/11/2011 11/11/2011
666996 1511 EL 13/11/2011 17/11/2011
755485 1787 SL 01/11/2011 14/11/2011
758545 1787 SL 15/11/2011 03/12/2011
796956 1954 AB 09/11/2011 09/11/2011
799656 1367 AB 09/11/2011 09/11/2011
808845 1527 EL 16/11/2011 16/11/2011
823323 1527 EL 17/11/2011 17/11/2011
823669 1527 EL 18/11/2011 18/11/2011
899555 1123 AB 09/11/2011 09/12/2011
990990 1511 AB 12/11/2011 12/11/2011
Теперь я хочу отчет, созданный с помощью хранимой процедуры, суммирующим при отсутствии дней для конкретного отсутствие кода для каждого месяца в данном году, например, если я хочу знать итоги отсутствия в 2011 году из предыдущей таблицы я получить что-то похожее на:
Month TotalDays
---------------------------------
JAN 2011 201
FEB 2011 36
MAR 2011 67
APR 2011 91
....
хранимая процедура будет иметь два Params (@ Год INT, @AbsCode NVARCHAR (3)).
Обратите внимание, что иногда запись перекрывается еще на один месяц (например, первая строка в таблице примеров), и это следует учитывать отдельно для каждого месяца. Я пробовал использовать петли, но не повезло. Я настолько слаб в TSQL.
UPDATE
Сейчас я использую пользовательскую функцию значения скалярного и хранимую процедуру для выполнения этой работы, его уродливые и трудно проследить. Вот это каким-либо образом:
Функция пользователя:
ALTER FUNCTION [dbo].[GetActivityTotalDaysInMonth]
(
@ActivityCode CHAR(3)
,@Year INT
,@Month INT
)
RETURNS INT
AS
BEGIN
DECLARE @FirstDayOfMonth DATETIME
DECLARE @LastDayOfMonth DATETIME
SET @FirstDayOfMonth = CAST(CAST(@Year AS varchar) + '-' + CAST(@Month AS varchar) + '-' + CAST(1 AS varchar) AS DATETIME)
SET @LastDayOfMonth = DATEADD(s, -1, DATEADD(M, 1, @FirstDayOfMonth))
DECLARE @TotalDays INT
SELECT @TotalDays =
SUM(DATEDIFF(DAY,
(CASE WHEN ActivityDateFrom < @FirstDayOfMonth THEN @FirstDayOfMonth ELSE ActivityDateFrom END)
, (CASE WHEN ActivityDateTo > @LastDayOfMonth THEN @LastDayOfMonth ELSE ActivityDateTo END))+1)
FROM Activities
WHERE
[email protected]
AND ((ActivityDateFrom < @FirstDayOfMonth AND ActivityDateTo >= @FirstDayOfMonth)
OR (ActivityDateFrom >= @FirstDayOfMonth AND ActivityDateTo <= @LastDayOfMonth)
OR (ActivityDateFrom <= @LastDayOfMonth AND ActivityDateTo > @LastDayOfMonth))
RETURN @TotalDays
END
Теперь я называю эту функцию внутри цикла в хранимой процедуре:
ALTER PROCEDURE GetAnnualActivityTotalDays
(
@ActivityCode CHAR(3)
,@Year INT
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Stats TABLE
([Month] NVARCHAR(50), TotalDays INT)
DECLARE @MonthNo INT
DECLARE @Month DATETIME
SET @MonthNo = 1
WHILE @MonthNo <= 12
BEGIN
SET @Month = CAST(CAST(@Year AS varchar) + '-' + CAST(@MonthNo AS varchar) + '-' + CAST(1 AS varchar) AS DATETIME)
INSERT INTO @Stats ([Month], TotalDays)
SELECT UPPER(SUBSTRING(DATENAME(mm, @Month), 1, 3)) + ', ' + CAST(@Year AS NVARCHAR),
dbo.GetActivityTotalDaysInMonth(@ActivityCode
,@Year
,@MonthNo
,@Base)
SET @MonthNo = @MonthNo + 1
END
SELECT * FROM @Stats
END
Как вы можете видеть, что это некрасиво код, который Я считаю, что это можно сделать более простым способом. Любые предложения?
Что вы сделали до сих пор? –
Это довольно неприятная структура стола. Ввод таких диапазонов даты, а не отдельных дат может привести к тому, что всевозможные проблемы, такие как сотрудник 123, отсутствуют дважды в одну дату (поскольку диапазоны перекрываются). – JNK
@JNK, Нет, этого никогда не произойдет. Эта таблица используется для всех видов деятельности, а не только отсутствия и ее так полезной в этом дизайне, единственной проблемой, которая была до сих пор, является это. –