【发布时间】:2018-05-10 20:15:09
【问题描述】:
我正在开发一个 T-SQL 函数来返回特定时期内每个月的两个类别(注册和验证)的计数。仅当每个月的计数至少大于 1 时,以下函数才有效。我想做的是给定源表中找到的最低日期的开始日期、今天的结束日期和给定的月数,返回给定用户的注册计数和验证计数月份开始和结束期间之间的 id。
理想情况下,我希望将范围周期数据格式化为MMM-YYYY。对于在此期间没有计数的任何月份,我希望这些值显示为 0。
RELEASED_DATETIME 是INTERACTION_SESSION_T 表中的DATETIME 列。
有效期为 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
);
谁能指出我正确的方向,或提供帮助?
【问题讨论】:
标签: sql sql-server tsql sql-server-2016