Я работаю над функцией T-SQL, которая возвращает количество двух категорий (регистрируется и проверяется) для каждого месяца в течение определенного периода. Приведенная ниже функция работает только в том случае, если для каждого месяца существует хотя бы число, большее 1. То, что я хотел бы сделать, это дать дату начала с самой низкой датой, найденной в исходной таблице, дату окончания сегодня и заданное количество месяцев, вернуть количество зарегистрированных и количество проверок для данного пользователя. идентификатор между началом и концом месяца.
В идеале я хотел бы, чтобы данные за период были отформатированы как MMM-YYYY
. Для любых месяцев в течение этого периода, когда отсчетов нет, я бы хотел, чтобы эти значения показывали счет 0. 0. 1004 *
RELEASED_DATETIME
- это столбец DATETIME
в таблице INTERACTION_SESSION_T
.
Действительные данные за 7 месяцев
RANGEPERIOD | ENROLLS | VERIFIES
------------+---------+---------
Nov-2017 | 15 | 15
Dec-2017 | 150 | 2582
Jan-2018 | 0 | 0
Feb-2018 | 0 | 98
Mar-2018 | 10 | 0
Apr-2018 | 8 | 0
May-2018 | 12 | 85
Мой код:
CREATE FUNCTION [dbo].[SVE_GET_ENROL_VERIFY_COUNT_MONTH_R]
(@DEV_ID INT, @NUM_MONTHS INT)
RETURNS @D TABLE (RANGEPERIOD VARCHAR(10),
TOTAL_ENROLLMENTS INT,
TOTAL_VERIFICATIONS INT)
AS
BEGIN
DECLARE @cur VARCHAR(12); -- stores the current record date the cursor points to
DECLARE @sdate DATETIME; -- start date of range
DECLARE @edate DATETIME; -- end date of range
DECLARE @tot_enrls INT; -- stores the @cur date's enrollment count
DECLARE @tot_verfs INT; -- stores the @cur date's verification count
SELECT @sdate = MIN(RELEASED_DATETIME)
FROM SVE_INTERACTION_SESSION_T
WHERE (DEVELOPER_OWNER_ID = @DEV_ID OR DEVELOPER_USER_ID = @DEV_ID)
AND RELEASED_DATETIME >= DATEADD(MONTH, -@NUM_MONTHS + 1, GETDATE())
SELECT @edate = GETDATE();
INSERT INTO @D (RANGEPERIOD)
(SELECT
RANGEPERIOD = CONCAT(SUBSTRING(datename(month, I.RELEASED_DATETIME), 1, 3 ), '-', YEAR(I.RELEASED_DATETIME))
FROM
SVE_INTERACTION_SESSION_T AS I WITH (NOLOCK)
INNER JOIN
SVE_APPLICATIONS_T AS A WITH (NOLOCK) ON I.APPLICATION_ID = A._ID
WHERE
(I.DEVELOPER_OWNER_ID = @DEV_ID OR I.DEVELOPER_USER_ID = @DEV_ID)
AND I.RELEASED_CODE = 'C'
AND A.IS_DELETED = 0
AND I.RELEASED_DATETIME >= @sdate
AND I.RELEASED_DATETIME < DATEADD(day, 1, @edate)
GROUP BY
YEAR(I.RELEASED_DATETIME), MONTH(I.RELEASED_DATETIME),
DATENAME(MONTH, I.RELEASED_DATETIME)
);
-- Start of cursor
DECLARE DTY CURSOR FOR
SELECT RANGEPERIOD FROM @d
OPEN DTY
-- Fetch the first row
FETCH NEXT FROM DTY INTO @cur
WHILE @@FETCH_STATUS = 0
BEGIN
SET @tot_enrls = (SELECT COUNT(DISTINCT I._ID) AS TOT_ENROLLS
FROM SVE_INTERACTION_SESSION_T AS I WITH (NOLOCK)
INNER JOIN SVE_ENROLL_SESSION_T AS E WITH (NOLOCK) ON E.INTERACTION_SESSION_ID = I._ID
INNER JOIN SVE_APPLICATIONS_T AS A WITH (NOLOCK) ON I.APPLICATION_ID = A._ID
WHERE
(I.DEVELOPER_OWNER_ID = @DEV_ID OR I.DEVELOPER_USER_ID = @DEV_ID)
AND E.INSTANCE_ID = 0
AND E.COMPLETION_STATUS = 'T'
AND I.RELEASED_CODE = 'C'
AND A.IS_DELETED = 0
AND CONCAT( SUBSTRING( datename( month, I.RELEASED_DATETIME ), 1, 3 ), '-', YEAR( I.RELEASED_DATETIME ) ) = @cur
);
SET @tot_verfs = (
SELECT
COUNT( DISTINCT I._ID ) AS TOT_VERIFYS
FROM
SVE_INTERACTION_SESSION_T AS I WITH (NOLOCK) INNER JOIN SVE_VERIFY_SESSION_T AS V WITH (NOLOCK)
ON
V.INTERACTION_SESSION_ID = I._ID INNER JOIN SVE_APPLICATIONS_T AS A WITH (NOLOCK)
ON
I.APPLICATION_ID = A._ID
WHERE
( I.DEVELOPER_OWNER_ID = @DEV_ID OR I.DEVELOPER_USER_ID = @DEV_ID ) AND
V.INSTANCE_ID = 0 AND
V.COMPLETION_STATUS = 'S' AND
I.RELEASED_CODE = 'C' AND
A.IS_DELETED = 0 AND
CONCAT( SUBSTRING( datename( month, I.RELEASED_DATETIME ), 1, 3 ), '-', YEAR( I.RELEASED_DATETIME ) ) = @cur
);
-- Updates the fields in the return table
UPDATE
@d
SET
TOTAL_ENROLLMENTS = @tot_enrls,
TOTAL_VERIFICATIONS = @tot_verfs
WHERE
RANGEPERIOD = @cur
-- Fetch the next row and repeat the above process
FETCH NEXT FROM DTY INTO @cur
END
-- Updates the return table by setting all null values to 0 for better visuals
UPDATE @d SET TOTAL_ENROLLMENTS = 0, TOTAL_VERIFICATIONS = 0 WHERE TOTAL_ENROLLMENTS IS NULL
RETURN
END
Возможно, стоит отметить, что я уже делал что-то подобное в течение ряда дней, и это работает безупречно. Разница лишь в том, что я использую таблицу календаря для функции диапазона дня. Я знаю, что секретный соус находится во временной таблице, которая создается из календарной таблицы, в которой я пытался изменить функцию месяца, чтобы она подходила, но в этот момент я растерялся
Вот дневная функция, которая прекрасно работает
ALTER FUNCTION [dbo].[SVE_GET_ENROL_VERIFY_COUNT_R]( @DEV_ID INT, @NUM_DAYS INT )
RETURNS @D TABLE
(
RANGEDATE DATE,
TOTAL_ENROLLMENTS INT,
TOTAL_VERIFICATIONS INT
)
AS
BEGIN
DECLARE @s DATE; -- start date of range
DECLARE @e DATE; -- end date of range
DECLARE @cur DATE; -- stores the current record date the cursor points to
DECLARE @tot_enrls INT; -- stores the @cur date's enrollment count
DECLARE @tot_verfs INT; -- stores the @cur date's verification count
SET @e = getdate(); -- stores today's date as the end date
SET @s = DATEADD( dd, -( @NUM_DAYS-1 ), @e ); -- Subtract 1 from the incoming day and setup the accurate range of days
INSERT INTO @d ( RANGEDATE )
(
SELECT
RANGEDATE = c.d
FROM
Calendar AS c LEFT OUTER JOIN SVE_INTERACTION_SESSION_T as I WITH (NOLOCK)
ON
I.RELEASED_DATETIME >= @s AND I.RELEASED_DATETIME < = @e AND
c.d = CONVERT( DATE, I.RELEASED_DATETIME )
WHERE
c.d >= @s AND c.d <= @e
GROUP BY c.d
);
-- Start of cursor
DECLARE DTY CURSOR FOR
SELECT RANGEDATE FROM @d
OPEN DTY
-- Fetch the first row
FETCH NEXT FROM DTY INTO @cur
WHILE @@FETCH_STATUS = 0
BEGIN
-- Gets the developer's enrollment count for the current record's day
SET @tot_enrls = (
SELECT
COUNT( DISTINCT I._ID ) AS TOT_ENROLLS
FROM
SVE_INTERACTION_SESSION_T AS I WITH (NOLOCK) INNER JOIN SVE_ENROLL_SESSION_T AS E WITH (NOLOCK)
ON
E.INTERACTION_SESSION_ID = I._ID INNER JOIN SVE_APPLICATIONS_T AS A WITH (NOLOCK)
ON
I.APPLICATION_ID = A._ID
WHERE
( I.DEVELOPER_OWNER_ID = @DEV_ID OR I.DEVELOPER_USER_ID = @DEV_ID ) AND
E.INSTANCE_ID = 0 AND
E.COMPLETION_STATUS = 'T' AND
I.RELEASED_CODE = 'C' AND
A.IS_DELETED = 0 AND
I.RELEASED_DATETIME >= @cur AND
I.RELEASED_DATETIME < dateadd(day,1,@cur)
);
-- Gets the developer's verification count for the current record's day
SET @tot_verfs = (
SELECT
COUNT( DISTINCT I._ID ) AS TOT_VERIFYS
FROM
SVE_INTERACTION_SESSION_T AS I WITH (NOLOCK) INNER JOIN SVE_VERIFY_SESSION_T AS V WITH (NOLOCK)
ON
V.INTERACTION_SESSION_ID = I._ID INNER JOIN SVE_APPLICATIONS_T AS A WITH (NOLOCK)
ON
I.APPLICATION_ID = A._ID
WHERE
( I.DEVELOPER_OWNER_ID = @DEV_ID OR I.DEVELOPER_USER_ID = @DEV_ID ) AND
V.INSTANCE_ID = 0 AND
V.COMPLETION_STATUS = 'S' AND
I.RELEASED_CODE = 'C' AND
A.IS_DELETED = 0 AND
I.RELEASED_DATETIME >= @cur AND
I.RELEASED_DATETIME < dateadd(day,1,@cur)
);
-- Updates the fields in the return table
UPDATE
@d
SET
TOTAL_ENROLLMENTS = @tot_enrls,
TOTAL_VERIFICATIONS = @tot_verfs
WHERE
RANGEDATE = @cur
-- Fetch the next row and repeat the above process
FETCH NEXT FROM DTY INTO @cur
END
-- Updates the return table by setting all null values to 0 for better visuals
UPDATE @d SET TOTAL_ENROLLMENTS = 0, TOTAL_VERIFICATIONS = 0 WHERE TOTAL_ENROLLMENTS IS NULL
RETURN
END
Вот так выглядит моя модификация для периода месяца, в которой используется таблица календаря, но это эпическая ошибка, поскольку она возвращает только одну строку.
INSERT INTO @d ( RANGEPERIOD )
(
SELECT
RANGEPERIOD = CONCAT( SUBSTRING( datename( month, c.d ), 1, 3 ), '-', YEAR( c.d ) )
FROM
Calendar AS c LEFT OUTER JOIN SVE_INTERACTION_SESSION_T as I WITH (NOLOCK)
ON
(YEAR(I.RELEASED_DATETIME) >= YEAR(@s) AND MONTH(I.RELEASED_DATETIME) >= MONTH(@s)) AND (YEAR(I.RELEASED_DATETIME) <= YEAR(@e) AND MONTH(I.RELEASED_DATETIME) <= MONTH(@e)) AND
c.d = CONVERT( DATE, I.RELEASED_DATETIME )
WHERE
(YEAR(c.d) >= YEAR(@s) AND MONTH(c.d) >= MONTH(@s)) AND (YEAR(c.d) <= YEAR(@e) AND MONTH(c.d) <= MONTH(@e))
GROUP BY c.d
);
Может ли кто-нибудь указать мне правильное направление или оказать помощь?