Количество строк по месяцам за период - PullRequest
0 голосов
/ 10 мая 2018

Я работаю над функцией 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
);

Может ли кто-нибудь указать мне правильное направление или оказать помощь?

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Если кто-то заинтересован в решении этой проблемы, смотрите мой код ниже. Возможно, это не самый эффективный способ, но сейчас я не могу придумать другого решения.

CREATE FUNCTION [dbo].[SVE_GET_ENROL_VERIFY_COUNT_MONTH_R]( @DEV_ID INT, @NUM_MONTHS INT )
RETURNS @M TABLE
(
    RANGEPERIOD         VARCHAR(10),
    TOTAL_ENROLLMENTS   INT,
    TOTAL_VERIFICATIONS INT
)
AS
BEGIN
    -- We need to declare a temporary table to hold the days from the period
    DECLARE @D TABLE(
        RANGEDATE         DATETIME,
        TOTAL_ENROLLMENTS   INT,
        TOTAL_VERIFICATIONS INT
    )

    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 = DATEADD( MONTH, -( @NUM_MONTHS-1 ), GETDATE() )
    SELECT @edate = GETDATE(); 

    -- First fill the temp day table with dates from the calendar
    INSERT INTO @D ( RANGEDATE ) (
        SELECT
            RANGEDATE = CONVERT( DATE, c.d )
        FROM
            Calendar AS c
        WHERE
            c.d >= @sdate AND c.d <= @edate
        GROUP BY c.d
    )

    -- Next we fill our return table with our grouped by formatted dates 
    INSERT INTO @M ( RANGEPERIOD ) (
        SELECT
            RANGEPERIOD = CONCAT( SUBSTRING( datename( month, D.RANGEDATE ), 1, 3 ), '-', YEAR( D.RANGEDATE ) )
        FROM
            @D AS D
        GROUP BY year( D.RANGEDATE ), month( D.RANGEDATE ), datename( month, D.RANGEDATE )
    )

 -- Start of cursor
    DECLARE DTY CURSOR FOR

    SELECT RANGEPERIOD FROM @M

    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
            @M
        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 @M SET TOTAL_ENROLLMENTS = 0, TOTAL_VERIFICATIONS = 0 WHERE TOTAL_ENROLLMENTS IS NULL

    RETURN
END
0 голосов
/ 11 мая 2018

Я не могу дать вам конкретное замечание, но могу дать вам несколько советов по решению вашей проблемы.Я предполагаю, что вам нужны результаты в этом формате.(МММ-ГГГГ)

В запросе на изменение вы можете удалить таблицу SVE_INTERACTION_SESSION_T.

Поскольку

  1. Вы не выбрали столбец из этой таблицы.
  2. Вы использовали LEFT JOIN, тогда ваш результат не получает эффекта от вашего состояния.Результат по-прежнему исходит из всей таблицы календаря

    SELECT RANGEPERIOD = CONCAT(SUBSTRING(datename(MONTH, c.d), 1, 3), '-', YEAR(c.d))
    FROM Calendar AS c
    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
    
...