diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql index 12357075e..a4c82c855 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql @@ -6,6 +6,7 @@ -- Modification History -- 01-10-2025 SA added assesment score and passmark and provider details -- 05-02-2025 SA TD-6860 : Fixed the null issue with the search history +-- 31-03-2026 OA TD-7057 Script Optimization ------------------------------------------------------------------------------- CREATE PROCEDURE [activity].[GetUserInProgressLearningActivities] ( @userId INT @@ -15,66 +16,90 @@ BEGIN ;WITH CTERecentActivities AS ( SELECT - ra.Id AS ActivityId, - ara.LaunchResourceActivityId AS LaunchResourceActivityId, - ra.UserId AS UserId, - ra.ResourceId AS ResourceId, - r.CurrentResourceVersionId AS ResourceVersionId, - CASE WHEN r.CurrentResourceVersionId = ra.ResourceVersionId THEN 1 ELSE 0 END AS IsCurrentResourceVersion, - ( - SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - WHERE rr.ResourceId = rv.ResourceId --AND rr.Deleted = 0 - ) AS ResourceReferenceID, - ra.MajorVersion AS MajorVersion, - ra.MinorVersion AS MinorVersion, - ra.NodePathId AS NodePathId, - r.ResourceTypeId AS ResourceType, - rv.Title AS Title, - --rv.[Description] AS ResourceDescription, - rv.CertificateEnabled AS CertificateEnabled, - ISNULL(ara.ActivityStatusId, ra.ActivityStatusId) AS ActivityStatus, - ra.ActivityStart AS ActivityDate, - -- ara.ActivityEnd, - ISNULL(ara.DurationSeconds, 0) AS ActivityDurationSeconds, - ara.Score AS ScorePercentage, - arv.AssessmentType AS AssessmentType, - arv.PassMark AS AssessmentPassMark, - asra.score AS AssesmentScore, - mar.SecondsPlayed AS SecondsPlayed, - ISNULL( CAST(mar.PercentComplete AS INT) ,0) AS PercentComplete, - sa.CmiCoreLesson_status AS CmiCoreLessonstatus, - sa.CmiCoreScoreMax AS CmiCoreScoreMax, - sa.CmiCoreSession_time AS CmiCoreSessiontime, - sa.DurationSeconds AS DurationSeconds, - rpAgg.ProvidersJson, - ROW_NUMBER() OVER (PARTITION BY ra.ResourceId ORDER BY ISNULL(ara.ActivityEnd, ra.ActivityStart) DESC) AS rn - FROM activity.ResourceActivity ra - LEFT JOIN activity.ResourceActivity ara ON ara.LaunchResourceActivityId = ra.Id - INNER JOIN [resources].[Resource] r ON ra.ResourceId = r.Id - INNER JOIN [resources].[ResourceVersion] rv ON rv.Id = ra.ResourceVersionId AND rv.Deleted = 0 - LEFT JOIN ( - SELECT - rp.ResourceVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE p.Deleted = 0 and rp.Deleted = 0 - GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId - LEFT JOIN [resources].[AssessmentResourceVersion] arv ON arv.ResourceVersionId = ra.ResourceVersionId - LEFT JOIN [activity].[AssessmentResourceActivity] asra ON asra.ResourceActivityId = ra.Id - LEFT JOIN [activity].[MediaResourceActivity] mar ON mar.ResourceActivityId = ra.Id - LEFT JOIN [activity].[ScormActivity] sa ON sa.ResourceActivityId = ra.Id - WHERE ra.LaunchResourceActivityId IS NULL AND ra.userid = @userId - AND ra.deleted = 0 - AND r.ResourceTypeId IN(6) --AND ra.ActivityStart >= DATEADD(MONTH, -6, SYSDATETIMEOFFSET()) -) + ra.Id AS ActivityId, + ara.LaunchResourceActivityId AS LaunchResourceActivityId, + ra.UserId AS UserId, + ra.ResourceId AS ResourceId, + r.CurrentResourceVersionId AS ResourceVersionId, + CASE WHEN r.CurrentResourceVersionId = ra.ResourceVersionId THEN 1 ELSE 0 END AS IsCurrentResourceVersion, + + rrRef.OriginalResourceReferenceId AS ResourceReferenceID, + + ra.MajorVersion AS MajorVersion, + ra.MinorVersion AS MinorVersion, + ra.NodePathId AS NodePathId, + r.ResourceTypeId AS ResourceType, + rv.Title AS Title, + rv.CertificateEnabled AS CertificateEnabled, + ISNULL(ara.ActivityStatusId, ra.ActivityStatusId) AS ActivityStatus, + ra.ActivityStart AS ActivityDate, + ISNULL(ara.DurationSeconds, 0) AS ActivityDurationSeconds, + ara.Score AS ScorePercentage, + arv.AssessmentType AS AssessmentType, + arv.PassMark AS AssessmentPassMark, + asra.score AS AssesmentScore, + mar.SecondsPlayed AS SecondsPlayed, + ISNULL(CAST(mar.PercentComplete AS INT), 0) AS PercentComplete, + sa.CmiCoreLesson_status AS CmiCoreLessonstatus, + sa.CmiCoreScoreMax AS CmiCoreScoreMax, + sa.CmiCoreSession_time AS CmiCoreSessiontime, + sa.DurationSeconds AS DurationSeconds, + rpAgg.ProvidersJson, + + ROW_NUMBER() OVER ( + PARTITION BY ra.ResourceId + ORDER BY ISNULL(ara.ActivityEnd, ra.ActivityStart) DESC + ) AS rn + + FROM activity.ResourceActivity ra + LEFT JOIN activity.ResourceActivity ara ON ara.LaunchResourceActivityId = ra.Id + INNER JOIN resources.Resource r ON ra.ResourceId = r.Id + INNER JOIN resources.ResourceVersion rv ON rv.Id = ra.ResourceVersionId AND rv.Deleted = 0 + + LEFT JOIN ( + SELECT + ResourceId, + MAX(Id) AS LatestRefId + FROM resources.ResourceReference + WHERE Deleted = 0 + GROUP BY ResourceId + ) rrLatest ON rrLatest.ResourceId = rv.ResourceId + + LEFT JOIN resources.ResourceReference rrRef + ON rrRef.Id = rrLatest.LatestRefId + + LEFT JOIN ( + SELECT + rp.ResourceVersionId, + ( + SELECT + p.Id, + p.Name, + p.Description, + p.Logo + FROM resources.ResourceVersionProvider rp2 + JOIN hub.Provider p ON p.Id = rp2.ProviderId + WHERE rp2.ResourceVersionId = rp.ResourceVersionId + AND rp2.Deleted = 0 + AND p.Deleted = 0 + FOR JSON PATH + ) AS ProvidersJson + FROM resources.ResourceVersionProvider rp + GROUP BY rp.ResourceVersionId + ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId + + + LEFT JOIN resources.AssessmentResourceVersion arv ON arv.ResourceVersionId = ra.ResourceVersionId + LEFT JOIN activity.AssessmentResourceActivity asra ON asra.ResourceActivityId = ra.Id + LEFT JOIN activity.MediaResourceActivity mar ON mar.ResourceActivityId = ra.Id + LEFT JOIN activity.ScormActivity sa ON sa.ResourceActivityId = ra.Id + + WHERE ra.LaunchResourceActivityId IS NULL + AND ra.UserId = @userId + AND ra.Deleted = 0 + AND r.ResourceTypeId IN (6) +) + SELECT Top 8 ActivityId, LaunchResourceActivityId, UserId, diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Hierarchy/GetDashboardCatalogues.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Hierarchy/GetDashboardCatalogues.sql index 5120cbf10..78e2c976e 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Hierarchy/GetDashboardCatalogues.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Hierarchy/GetDashboardCatalogues.sql @@ -15,6 +15,7 @@ -- 27 Sep 2023 HV Included Paging and user accessed catalogues -- 13 Nov 2023 SA Included Node VersionId in also. -- 29 Sep 2025 SA Integrated the provider dertails +-- 31 Mar 2026 OA TD-7057 Script Optimization ------------------------------------------------------------------------------- CREATE PROCEDURE [hierarchy].[GetDashboardCatalogues] @DashboardType nvarchar(30), @@ -23,312 +24,232 @@ CREATE PROCEDURE [hierarchy].[GetDashboardCatalogues] @TotalRecords INT OUTPUT AS BEGIN - DECLARE @MaxPageNumber INT = 4 - DECLARE @FetchRows INT = 3 - DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows - DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows - - IF @PageNumber > 4 AND @DashboardType <> 'all-catalogues' - BEGIN - SET @PageNumber = @MaxPageNumber - END - - DECLARE @Catalogues TABLE (NodeId [int] NOT NULL PRIMARY KEY, NodeCount [int] NOT NULL) - - IF @DashboardType = 'popular-catalogues' - BEGIN - INSERT INTO @Catalogues - SELECT na.NodeId, Count( na.CatalogueNodeVersionId) NodeCount - FROM [hierarchy].[Node] n - JOIN [activity].[NodeActivity] na ON na.NodeId = n.Id AND na.CatalogueNodeVersionId = n.CurrentNodeVersionId - JOIN [hierarchy].[NodeVersion] nv ON nv.NodeId = n.Id - JOIN [hierarchy].[CatalogueNodeVersion] cnv ON cnv.NodeVersionId = nv.Id - WHERE n.Id <> 1 AND n.Hidden = 0 AND n.Deleted = 0 AND cnv.Deleted = 0 AND nv.VersionStatusId = 2 AND na.NodeId !=113 /* ORCHA - temporary removal */ - GROUP BY na.NodeId - SELECT - nv.NodeId - ,cnv.Id AS NodeVersionId - ,cnv.Name - ,cnv.Description - ,cnv.BannerUrl - ,cnv.BadgeUrl - ,cnv.CardImageUrl - ,cnv.Url - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,cpAgg.ProvidersJson - FROM @Catalogues tc - JOIN [hierarchy].[Node] n ON tc.NodeId = n.Id - JOIN [hierarchy].[NodeVersion] nv ON nv.NodeId = n.Id - JOIN [hierarchy].[CatalogueNodeVersion] cnv ON cnv.NodeVersionId = nv.Id - LEFT JOIN ( - SELECT - cnp.CatalogueNodeVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM hierarchy.CatalogueNodeVersionProvider cnp - JOIN hub.Provider p ON p.Id = cnp.ProviderId - WHERE p.Deleted = 0 and cnp.Deleted = 0 - GROUP BY cnp.CatalogueNodeVersionId - ) cpAgg ON cpAgg.CatalogueNodeVersionId = cnv.Id - JOIN hub.Scope s ON n.Id = s.CatalogueNodeId - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = nv.NodeId - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE n.Id <> 1 AND n.Hidden = 0 AND n.Deleted = 0 AND cnv.Deleted = 0 AND nv.VersionStatusId = 2 AND s.Deleted = 0 - ORDER BY NodeCount DESC, cnv.Name - OFFSET @OffsetRows ROWS - FETCH NEXT @FetchRows ROWS ONLY - - SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM @Catalogues - END - ELSE IF @DashboardType = 'recent-catalogues' - BEGIN - SELECT - nv.NodeId - ,cnv.Id AS NodeVersionId - ,cnv.Name - ,cnv.Description - ,cnv.BannerUrl - ,cnv.BadgeUrl - ,cnv.CardImageUrl - ,cnv.Url - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,cpAgg.ProvidersJson - INTO #recentcatalogues - FROM [hierarchy].[Node] n - JOIN [hierarchy].[NodeVersion] nv ON nv.NodeId = n.Id - JOIN [hierarchy].[CatalogueNodeVersion] cnv ON cnv.NodeVersionId = nv.Id - LEFT JOIN ( - SELECT - cnp.CatalogueNodeVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM hierarchy.CatalogueNodeVersionProvider cnp - JOIN hub.Provider p ON p.Id = cnp.ProviderId - WHERE p.Deleted = 0 and cnp.Deleted = 0 - GROUP BY cnp.CatalogueNodeVersionId - ) cpAgg ON cpAgg.CatalogueNodeVersionId = cnv.Id - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = nv.NodeId - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE n.Id <> 1 AND n.Hidden = 0 AND n.Deleted = 0 AND cnv.Deleted = 0 AND nv.VersionStatusId = 2 AND cnv.LastShownDate IS NOT NULL - ORDER BY cnv.LastShownDate DESC - - SELECT rc.* FROM #recentcatalogues rc - ORDER BY rc.NodeId DESC - OFFSET @OffsetRows ROWS - FETCH NEXT @FetchRows ROWS ONLY - - SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM #recentcatalogues - END - ELSE IF @DashboardType = 'highly-contributed-catalogues' - BEGIN - INSERT INTO @Catalogues - select nr.NodeId, count(*) AS NodeCount - from [hierarchy].[Node] n - JOIN hierarchy.NodeResource nr ON nr.NodeId = n.Id - JOIN resources.Resource r ON r.Id = nr.ResourceId - JOIN resources.ResourceVersion rv ON rv.Id = r.CurrentResourceVersionId AND rv.VersionStatusId = 2 and rv.Deleted = 0 - JOIN hierarchy.Publication p ON p.Id = nr.PublicationId AND p.Deleted = 0 - where n.Id <> 1 AND nr.Deleted = 0 AND n.Deleted = 0 AND n.Hidden = 0 - GROUP BY nr.NodeId - - SELECT - nv.NodeId - ,cnv.Id AS NodeVersionId - ,cnv.Name - ,cnv.Description - ,cnv.BannerUrl - ,cnv.BadgeUrl - ,cnv.CardImageUrl - ,cnv.Url - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,(SELECT Sum(rvrs.AverageRating) - FROM [resources].[ResourceVersionRatingSummary] rvrs - WHERE rvrs.resourceversionid IN - (SELECT r.currentresourceversionid - FROM [resources].[Resource] r - WHERE r.deleted = 0 AND r.id IN - (SELECT nr.resourceid - FROM [hierarchy].[NodeResource] nr - WHERE nr.nodeid = n.id)) - ) AS AverageRating - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,cpAgg.ProvidersJson - FROM @Catalogues tc - JOIN [hierarchy].[Node] n ON tc.NodeId = n.Id - JOIN [hierarchy].[NodeVersion] nv ON nv.NodeId = n.Id - JOIN [hierarchy].[CatalogueNodeVersion] cnv ON cnv.NodeVersionId = nv.Id - LEFT JOIN ( - SELECT - cnp.CatalogueNodeVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM hierarchy.CatalogueNodeVersionProvider cnp - JOIN hub.Provider p ON p.Id = cnp.ProviderId - WHERE p.Deleted = 0 and cnp.Deleted = 0 - GROUP BY cnp.CatalogueNodeVersionId - ) cpAgg ON cpAgg.CatalogueNodeVersionId = cnv.Id - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = nv.NodeId - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE n.Id <> 1 AND n.Hidden = 0 AND n.Deleted = 0 AND cnv.Deleted = 0 AND nv.VersionStatusId = 2 - ORDER BY tc.NodeCount DESC, AverageRating DESC, cnv.Name - OFFSET @OffsetRows ROWS - FETCH NEXT @FetchRows ROWS ONLY - - SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM @Catalogues - END - ELSE IF @DashboardType = 'all-catalogues' - BEGIN - SET @FetchRows = 9 - SET @OffsetRows = (@PageNumber - 1) * @FetchRows - IF @PageNumber = -1 - BEGIN - SET @FetchRows = 100000 - SET @OffsetRows = 0 - END - SELECT - nv.NodeId - ,cnv.Id AS NodeVersionId - ,cnv.Name - ,cnv.Description - ,cnv.BannerUrl - ,cnv.BadgeUrl - ,cnv.CardImageUrl - ,cnv.Url - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,cpAgg.ProvidersJson - FROM [hierarchy].[Node] n - JOIN [hierarchy].[NodeVersion] nv ON nv.NodeId = n.Id - JOIN [hierarchy].[CatalogueNodeVersion] cnv ON cnv.NodeVersionId = nv.Id - LEFT JOIN ( - SELECT - cnp.CatalogueNodeVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM hierarchy.CatalogueNodeVersionProvider cnp - JOIN hub.Provider p ON p.Id = cnp.ProviderId - WHERE p.Deleted = 0 and cnp.Deleted = 0 - GROUP BY cnp.CatalogueNodeVersionId - ) cpAgg ON cpAgg.CatalogueNodeVersionId = cnv.Id - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = nv.NodeId - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE n.Id <> 1 AND n.Hidden = 0 AND n.Deleted = 0 AND cnv.Deleted = 0 AND nv.VersionStatusId = 2 - ORDER BY cnv.Name - OFFSET @OffsetRows ROWS - FETCH NEXT @FetchRows ROWS ONLY - - SELECT @TotalRecords = Count(1) - FROM [hierarchy].[Node] n - JOIN [hierarchy].[NodeVersion] nv ON nv.NodeId = n.Id - JOIN [hierarchy].[CatalogueNodeVersion] cnv ON cnv.NodeVersionId = nv.Id - WHERE n.Id <> 1 AND n.Hidden = 0 AND n.Deleted = 0 AND cnv.Deleted = 0 AND nv.VersionStatusId = 2 - END - ELSE IF @DashboardType = 'my-catalogues' - BEGIN - DECLARE @MyActivity TABLE (NodeId [int] NOT NULL PRIMARY KEY, ResourceActivityId [int] NOT NULL, CatalogueNodeId [INT] NOT NULL) - - INSERT INTO @MyActivity - SELECT np.NodeId, MAX(ra.Id) ResourceActivityId, CatalogueNodeId - FROM - activity.ResourceActivity ra - JOIN [hierarchy].[NodePath] np ON np.id = ra.NodePathId - WHERE ra.UserId = @UserId AND np.NodeId <> 1 - GROUP BY np.NodeId , CatalogueNodeId - ORDER BY ResourceActivityId DESC - - SELECT DISTINCT - nv.NodeId - ,cnv.Id AS NodeVersionId - ,cnv.Name - ,cnv.Description - ,cnv.BannerUrl - ,cnv.BadgeUrl - ,cnv.CardImageUrl - ,cnv.Url - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,cpAgg.ProvidersJson - FROM @MyActivity ma - JOIN [hierarchy].[Node] n ON ma.NodeId = n.Id - JOIN [hierarchy].[NodeVersion] nv ON nv.NodeId = ma.CatalogueNodeId - LEFT JOIN [hierarchy].[CatalogueNodeVersion] cnv ON cnv.NodeVersionId = nv.Id - LEFT JOIN ( - SELECT - cnp.CatalogueNodeVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM hierarchy.CatalogueNodeVersionProvider cnp - JOIN hub.Provider p ON p.Id = cnp.ProviderId - WHERE p.Deleted = 0 and cnp.Deleted = 0 - GROUP BY cnp.CatalogueNodeVersionId - ) cpAgg ON cpAgg.CatalogueNodeVersionId = cnv.Id - INNER JOIN hub.Scope s ON ma.CatalogueNodeId = s.CatalogueNodeId - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = nv.NodeId - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE - n.Id <> 1 - AND n.Hidden = 0 AND n.Deleted = 0 AND cnv.Deleted = 0 AND nv.VersionStatusId = 2 AND s.Deleted = 0 - ORDER BY nv.NodeId DESC, cnv.Name - OFFSET @OffsetRows ROWS - FETCH NEXT @FetchRows ROWS ONLY - - ;WITH CTETEMP AS (SELECT DISTINCT NV.NodeId - FROM @MyActivity ma - JOIN [hierarchy].[Node] n ON ma.NodeId = n.Id - JOIN [hierarchy].[NodeVersion] nv ON nv.NodeId = ma.CatalogueNodeId - LEFT JOIN [hierarchy].[CatalogueNodeVersion] cnv ON cnv.NodeVersionId = nv.Id - INNER JOIN hub.Scope s ON ma.CatalogueNodeId = s.CatalogueNodeId - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = nv.NodeId - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE - n.Id <> 1 - AND n.Hidden = 0 AND n.Deleted = 0 AND cnv.Deleted = 0 AND nv.VersionStatusId = 2 AND s.Deleted = 0) - - SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END - FROM CTETEMP - END + SET NOCOUNT ON; + + DECLARE @MaxPageNumber INT = 4; + DECLARE @FetchRows INT = 3; + + IF @DashboardType = 'all-catalogues' + SET @FetchRows = CASE WHEN @PageNumber = -1 THEN 100000 ELSE 9 END; + + IF @PageNumber > @MaxPageNumber AND @DashboardType <> 'all-catalogues' + SET @PageNumber = @MaxPageNumber; + + DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; + + -- Introduce shared temp table datasets for reuse everywhere: + + -- Providers aggregation + SELECT + cnp.CatalogueNodeVersionId, + JSON_QUERY('[' + STRING_AGG( + '{"Id":' + CAST(p.Id AS NVARCHAR) + + ',"Name":"' + p.Name + '"' + + ',"Description":"' + p.Description + '"' + + ',"Logo":"' + ISNULL(p.Logo,'') + '"}', + ',') + ']') AS ProvidersJson + INTO #Providers + FROM hierarchy.CatalogueNodeVersionProvider cnp + JOIN hub.Provider p ON p.Id = cnp.ProviderId + WHERE p.Deleted = 0 AND cnp.Deleted = 0 + GROUP BY cnp.CatalogueNodeVersionId; + + -- Auth lookup + SELECT DISTINCT CatalogueNodeId + INTO #Auth + FROM hub.RoleUserGroupView rug + JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId + WHERE rug.ScopeTypeId = 1 + AND rug.RoleId IN (1,2,3) + AND uug.Deleted = 0 + AND uug.UserId = @UserId; + + + SELECT + n.Id AS NodeId, + nv.Id AS NodeVersionId, + cnv.Id AS CatalogueNodeVersionId, + cnv.Name, + cnv.Description, + cnv.BannerUrl, + cnv.BadgeUrl, + cnv.CardImageUrl, + cnv.Url, + cnv.RestrictedAccess + INTO #BaseCatalogues + FROM hierarchy.Node n + JOIN hierarchy.NodeVersion nv ON nv.NodeId = n.Id AND nv.VersionStatusId = 2 AND nv.Deleted = 0 + JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 + WHERE n.Id <> 1 AND n.Hidden = 0 AND n.Deleted = 0; + + IF @DashboardType = 'popular-catalogues' + BEGIN + SELECT + na.NodeId, + COUNT(*) AS NodeCount + INTO #Popular + FROM activity.NodeActivity na + JOIN hierarchy.Node n ON n.Id = na.NodeId + WHERE + na.CatalogueNodeVersionId = n.CurrentNodeVersionId + AND n.Hidden = 0 + AND n.Deleted = 0 + AND na.NodeId <> 113 + GROUP BY na.NodeId; + + SELECT @TotalRecords = COUNT(*) FROM #Popular; + + SELECT + b.NodeId, + b.NodeVersionId, + b.Name, + b.Description, + b.BannerUrl, + b.BadgeUrl, + b.CardImageUrl, + b.Url, + b.RestrictedAccess, + + CAST(CASE + WHEN b.RestrictedAccess = 1 AND a.CatalogueNodeId IS NULL THEN 0 + ELSE 1 + END AS BIT) AS HasAccess, + + ub.Id AS BookMarkId, + CAST(ISNULL(ub.Deleted,1) ^ 1 AS BIT) AS IsBookmarked, + p.ProvidersJson + + FROM #Popular pop + JOIN #BaseCatalogues b ON b.NodeId = pop.NodeId + LEFT JOIN #Providers p ON p.CatalogueNodeVersionId = b.CatalogueNodeVersionId + LEFT JOIN #Auth a ON a.CatalogueNodeId = b.NodeId + LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = b.NodeId + + ORDER BY pop.NodeCount DESC, b.Name + OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; + END + + ELSE IF @DashboardType = 'recent-catalogues' + BEGIN + SELECT @TotalRecords = COUNT(*) + FROM #BaseCatalogues + WHERE CatalogueNodeVersionId IN ( + SELECT Id FROM hierarchy.CatalogueNodeVersion WHERE LastShownDate IS NOT NULL + ); + + SELECT + b.NodeId, + b.NodeVersionId, + b.Name, + b.Description, + b.BannerUrl, + b.BadgeUrl, + b.CardImageUrl, + b.Url, + b.RestrictedAccess, + + CAST(CASE + WHEN b.RestrictedAccess = 1 AND a.CatalogueNodeId IS NULL THEN 0 + ELSE 1 + END AS BIT) AS HasAccess, + + ub.Id, + CAST(ISNULL(ub.Deleted,1) ^ 1 AS BIT) AS IsBookmarked, + p.ProvidersJson + + FROM #BaseCatalogues b + LEFT JOIN #Providers p ON p.CatalogueNodeVersionId = b.CatalogueNodeVersionId + LEFT JOIN #Auth a ON a.CatalogueNodeId = b.NodeId + LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = b.NodeId + JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.Id = b.CatalogueNodeVersionId + WHERE cnv.LastShownDate IS NOT NULL + + ORDER BY cnv.LastShownDate DESC + OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; + END + + -- MY CATALOGUES with fixed MAX pattern + ELSE IF @DashboardType = 'my-catalogues' + BEGIN + WITH Latest AS ( + SELECT + np.NodeId, + np.CatalogueNodeId, + ROW_NUMBER() OVER ( + PARTITION BY np.NodeId + ORDER BY ra.Id DESC + ) rn + FROM activity.ResourceActivity ra + JOIN hierarchy.NodePath np ON np.Id = ra.NodePathId + WHERE ra.UserId = @UserId + ) + SELECT DISTINCT NodeId, CatalogueNodeId + INTO #MyCatalogues + FROM Latest + WHERE rn = 1; + + SELECT @TotalRecords = COUNT(*) FROM #MyCatalogues; + + SELECT + b.NodeId, + b.NodeVersionId, + b.Name, + b.Description, + b.BannerUrl, + b.BadgeUrl, + b.CardImageUrl, + b.Url, + b.RestrictedAccess, + + CAST(CASE + WHEN b.RestrictedAccess = 1 AND a.CatalogueNodeId IS NULL THEN 0 + ELSE 1 + END AS BIT) AS HasAccess, + + ub.Id, + CAST(ISNULL(ub.Deleted,1) ^ 1 AS BIT) AS IsBookmarked, + p.ProvidersJson + + FROM #MyCatalogues mc + JOIN #BaseCatalogues b ON b.NodeId = mc.CatalogueNodeId + LEFT JOIN #Providers p ON p.CatalogueNodeVersionId = b.CatalogueNodeVersionId + LEFT JOIN #Auth a ON a.CatalogueNodeId = b.NodeId + LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = b.NodeId + + ORDER BY b.NodeId DESC + OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; + END + + ELSE + BEGIN + SELECT @TotalRecords = COUNT(*) FROM #BaseCatalogues; + + SELECT + b.NodeId, + b.NodeVersionId, + b.Name, + b.Description, + b.BannerUrl, + b.BadgeUrl, + b.CardImageUrl, + b.Url, + b.RestrictedAccess, + + CAST(CASE + WHEN b.RestrictedAccess = 1 AND a.CatalogueNodeId IS NULL THEN 0 + ELSE 1 + END AS BIT) AS HasAccess, + + ub.Id, + CAST(ISNULL(ub.Deleted,1) ^ 1 AS BIT) AS IsBookmarked, + p.ProvidersJson + + FROM #BaseCatalogues b + LEFT JOIN #Providers p ON p.CatalogueNodeVersionId = b.CatalogueNodeVersionId + LEFT JOIN #Auth a ON a.CatalogueNodeId = b.NodeId + LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.NodeId = b.NodeId + + ORDER BY b.Name + OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; + END END \ No newline at end of file diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyInProgressDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyInProgressDashboardResources.sql index 59ddb3bff..1d448566e 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyInProgressDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyInProgressDashboardResources.sql @@ -85,19 +85,24 @@ BEGIN JOIN resources.resourceversion rv ON rv.id = ra.ResourceVersionId AND rv.Deleted = 0 JOIN Resources.Resource r ON r.Id = rv.ResourceId LEFT JOIN ( - SELECT - rp.ResourceVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE p.Deleted = 0 and rp.Deleted = 0 - GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId + SELECT + rp.ResourceVersionId, + ( + SELECT + p.Id, + p.Name, + p.Description, + p.Logo + FROM resources.ResourceVersionProvider rp2 + JOIN hub.Provider p ON p.Id = rp2.ProviderId + WHERE rp2.ResourceVersionId = rp.ResourceVersionId + AND rp2.Deleted = 0 + AND p.Deleted = 0 + FOR JSON PATH + ) AS ProvidersJson + FROM resources.ResourceVersionProvider rp + GROUP BY rp.ResourceVersionId +) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0 JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0 JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyLearningCertificatesDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyLearningCertificatesDashboardResources.sql index 8b141b80f..40446cc0c 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyLearningCertificatesDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyLearningCertificatesDashboardResources.sql @@ -82,19 +82,24 @@ JOIN activity.ResourceActivity ra ON ra.id = ma.ResourceActivityId JOIN resources.resourceversion rv ON rv.id = ra.ResourceVersionId AND rv.Deleted = 0 JOIN Resources.Resource r ON r.Id = rv.ResourceId LEFT JOIN ( - SELECT - rp.ResourceVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE p.Deleted = 0 and rp.Deleted = 0 - GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId + SELECT + rp.ResourceVersionId, + ( + SELECT + p.Id, + p.Name, + p.Description, + p.Logo + FROM resources.ResourceVersionProvider rp2 + JOIN hub.Provider p ON p.Id = rp2.ProviderId + WHERE rp2.ResourceVersionId = rp.ResourceVersionId + AND rp2.Deleted = 0 + AND p.Deleted = 0 + FOR JSON PATH + ) AS ProvidersJson + FROM resources.ResourceVersionProvider rp + GROUP BY rp.ResourceVersionId +) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0 JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0 JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql index 465cc84a3..787171f56 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql @@ -5,131 +5,225 @@ -- -- Modification History -- --- 24 Jun 2024 OA Initial Revision +-- 24 Jun 2024 OA Initial Revision -- 27 Jun 2024 SA Removed unused temp tables --- 29 Sep 2025 SA Integrated the provider dertails +-- 29 Sep 2025 SA Integrated the provider details +-- 31 Mar 2026 OA TD-7057 Script Optimization ------------------------------------------------------------------------------- CREATE PROCEDURE [resources].[GetMyRecentCompletedDashboardResources] - @UserId INT, - @PageNumber INT = 1, - @TotalRecords INT OUTPUT +( + @UserId INT, + @PageNumber INT = 1, + @TotalRecords INT OUTPUT +) AS BEGIN - DECLARE @MaxPageNumber INT = 4 - - IF @PageNumber > 4 - BEGIN - SET @PageNumber = @MaxPageNumber - END - - DECLARE @FetchRows INT = 3 - DECLARE @MaxRows INT = @MaxPageNUmber * @FetchRows - DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows - - DECLARE @MyActivity TABLE (ResourceId [int] NOT NULL PRIMARY KEY, ResourceActivityId [int] NOT NULL); - - INSERT INTO @MyActivity - SELECT TOP (@MaxRows) ra.ResourceId, MAX(ra.Id) ResourceActivityId - FROM - (SELECT a.Id,a.ResourceId,a.ResourceVersionId,a.LaunchResourceActivityId,a.UserId,a.ActivityStatusId,a.ActivityStart FROM activity.ResourceActivity a INNER JOIN (SELECT ResourceId, MAX(Id) as id FROM activity.ResourceActivity GROUP BY ResourceId ) AS b ON a.ResourceId = b.ResourceId AND a.id = b.id order by a.Id desc OFFSET 0 ROWS) ra - JOIN [resources].[Resource] r ON ra.ResourceId = r.Id - JOIN [resources].[ResourceVersion] rv ON rv.Id = ra.ResourceVersionId - LEFT JOIN [resources].[AssessmentResourceVersion] arv ON arv.ResourceVersionId = ra.ResourceVersionId - LEFT JOIN [activity].[AssessmentResourceActivity] ara ON ara.ResourceActivityId = COALESCE(ra.LaunchResourceActivityId, ra.Id) - LEFT JOIN [activity].[MediaResourceActivity] mar ON mar.ResourceActivityId = COALESCE(ra.LaunchResourceActivityId, ra.Id) - LEFT JOIN [activity].[ScormActivity] sa ON sa.ResourceActivityId = ra.Id - WHERE ra.UserId = @UserId - AND ( - (r.ResourceTypeId IN (2, 7) AND ra.ActivityStatusId = 3 AND ((mar.Id IS NOT NULL AND mar.PercentComplete = 100) OR ra.ActivityStart < '2020-09-07 00:00:00 +00:00')) - OR (r.ResourceTypeId = 6 AND (sa.CmiCoreLesson_status IN(3,5) OR (ra.ActivityStatusId IN(3, 5)))) - OR (r.ResourceTypeId = 11 AND ara.Score >= arv.PassMark OR ra.ActivityStatusId IN( 3, 5)) - OR (r.ResourceTypeId IN (1, 5, 8, 9, 10, 12) AND ra.ActivityStatusId = 3)) - GROUP BY ra.ResourceId - ORDER BY ResourceActivityId DESC - - SELECT r.Id AS ResourceId - ,( SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0 - ) AS ResourceReferenceID - ,r.CurrentResourceVersionId AS ResourceVersionId - ,r.ResourceTypeId AS ResourceTypeId - ,rv.Title - ,rv.Description - ,CASE - WHEN r.ResourceTypeId = 7 THEN - (SELECT vrv.DurationInMilliseconds from [resources].[VideoResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId) - WHEN r.ResourceTypeId = 2 THEN - (SELECT vrv.DurationInMilliseconds from [resources].[AudioResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId) - ELSE - NULL - END AS DurationInMilliseconds - ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName - ,cnv.Url AS Url - ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,rvrs.AverageRating - ,rvrs.RatingCount - ,rpAgg.ProvidersJson -FROM @MyActivity ma -JOIN activity.ResourceActivity ra ON ra.id = ma.ResourceActivityId -JOIN resources.resourceversion rv ON rv.id = ra.ResourceVersionId AND rv.Deleted = 0 -JOIN Resources.Resource r ON r.Id = rv.ResourceId -LEFT JOIN ( + SET NOCOUNT ON; + + DECLARE @MaxPageNumber INT = 4; + DECLARE @FetchRows INT = 3; + + IF @PageNumber > @MaxPageNumber + SET @PageNumber = @MaxPageNumber; + + DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; + DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; + + -- Temp table for activity + CREATE TABLE #MyActivity ( + ResourceId INT PRIMARY KEY, + ResourceActivityId INT + ); + + + -- Latest activity per resource per user + + WITH LatestActivity AS ( + SELECT + a.Id, + a.ResourceId, + a.ResourceVersionId, + a.LaunchResourceActivityId, + a.ActivityStatusId, + a.ActivityStart, + ROW_NUMBER() OVER ( + PARTITION BY a.ResourceId + ORDER BY a.Id DESC + ) AS rn + FROM activity.ResourceActivity a + WHERE a.UserId = @UserId + ) + INSERT INTO #MyActivity + SELECT TOP (@MaxRows) + la.ResourceId, + la.Id + FROM LatestActivity la + JOIN resources.Resource r ON r.Id = la.ResourceId + JOIN resources.ResourceVersion rv ON rv.Id = la.ResourceVersionId + LEFT JOIN resources.AssessmentResourceVersion arv + ON arv.ResourceVersionId = la.ResourceVersionId + LEFT JOIN activity.AssessmentResourceActivity ara + ON ara.ResourceActivityId = COALESCE(la.LaunchResourceActivityId, la.Id) + LEFT JOIN activity.MediaResourceActivity mar + ON mar.ResourceActivityId = COALESCE(la.LaunchResourceActivityId, la.Id) + LEFT JOIN activity.ScormActivity sa + ON sa.ResourceActivityId = la.Id + WHERE + la.rn = 1 + AND ( + (r.ResourceTypeId IN (2,7) + AND la.ActivityStatusId = 3 + AND ( + (mar.Id IS NOT NULL AND mar.PercentComplete = 100) + OR la.ActivityStart < '2020-09-07' + ) + ) + OR ( + r.ResourceTypeId = 6 + AND ( + sa.CmiCoreLesson_status IN (3,5) + OR la.ActivityStatusId IN (3,5) + ) + ) + OR ( + r.ResourceTypeId = 11 + AND ( + ara.Score >= arv.PassMark + OR la.ActivityStatusId IN (3,5) + ) + ) + OR ( + r.ResourceTypeId IN (1,5,8,9,10,12) + AND la.ActivityStatusId = 3 + ) + ) + ORDER BY la.Id DESC; + + + -- Total count + + SELECT @TotalRecords = COUNT(*) FROM #MyActivity; + + + -- Precompute ResourceReference lookup (replaces OUTER APPLY) + + CREATE TABLE #ResourceRefLookup ( + ResourceId INT NOT NULL, + NodeId INT NOT NULL, + OriginalResourceReferenceId INT NOT NULL, + PRIMARY KEY (ResourceId, NodeId) + ); + + INSERT INTO #ResourceRefLookup (ResourceId, NodeId, OriginalResourceReferenceId) + SELECT + x.ResourceId, + x.NodeId, + x.OriginalResourceReferenceId + FROM ( SELECT - rp.ResourceVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE p.Deleted = 0 and rp.Deleted = 0 - GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId -JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0 -JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0 -JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 -JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 -JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 -JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 -JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 -LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.ResourceReferenceId = (SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0) -LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId -ORDER BY ma.ResourceActivityId DESC, rv.Title -OFFSET @OffsetRows ROWS -FETCH NEXT @FetchRows ROWS ONLY - -SELECT @TotalRecords = CASE WHEN COUNT(ma.ResourceActivityId) > 12 THEN @MaxRows ELSE COUNT(*) END -FROM @MyActivity ma -JOIN activity.ResourceActivity ra ON ra.id = ma.ResourceActivityId -JOIN resources.resourceversion rv ON rv.id = ra.ResourceVersionId AND rv.Deleted = 0 -JOIN Resources.Resource r ON r.Id = rv.ResourceId -JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0 -JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0 -JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 -JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 -JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 -JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 -JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 -LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.ResourceReferenceId = (SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0) -LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - + rr.ResourceId, + np.NodeId, + rr.OriginalResourceReferenceId, + ROW_NUMBER() OVER ( + PARTITION BY rr.ResourceId, np.NodeId + ORDER BY rr.Id DESC + ) AS rn + FROM resources.ResourceReference rr + JOIN hierarchy.NodePath np + ON np.Id = rr.NodePathId + WHERE rr.Deleted = 0 + AND np.Deleted = 0 + ) x + WHERE x.rn = 1; + + + + -- Final SELECT + + SELECT + r.Id AS ResourceId, + rrRef.OriginalResourceReferenceId AS ResourceReferenceID, + r.CurrentResourceVersionId AS ResourceVersionId, + r.ResourceTypeId, + rv.Title, + rv.Description, + CASE + WHEN r.ResourceTypeId = 7 THEN vrv.DurationInMilliseconds + WHEN r.ResourceTypeId = 2 THEN arv2.DurationInMilliseconds + ELSE NULL + END AS DurationInMilliseconds, + CASE WHEN n.Id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName, + cnv.Url, + CASE WHEN n.Id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl, + cnv.RestrictedAccess, + CAST( + CASE + WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL + THEN 0 ELSE 1 + END AS BIT + ) AS HasAccess, + ub.Id AS BookMarkId, + CAST(ISNULL(ub.Deleted,1) ^ 1 AS BIT) AS IsBookmarked, + rvrs.AverageRating, + rvrs.RatingCount, + rpAgg.ProvidersJson + FROM #MyActivity ma + JOIN activity.ResourceActivity ra ON ra.Id = ma.ResourceActivityId + JOIN resources.ResourceVersion rv ON rv.Id = ra.ResourceVersionId AND rv.Deleted = 0 + JOIN resources.Resource r ON r.Id = rv.ResourceId + JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 + + -- Replaced OUTER APPLY + LEFT JOIN #ResourceRefLookup rrRef + ON rrRef.ResourceId = r.Id + AND rrRef.NodeId = nr.NodeId + + LEFT JOIN ( + SELECT + rp.ResourceVersionId, + ( + SELECT + p.Id, + p.Name, + p.Description, + p.Logo + FROM resources.ResourceVersionProvider rp2 + JOIN hub.Provider p ON p.Id = rp2.ProviderId + WHERE rp2.ResourceVersionId = rp.ResourceVersionId + AND rp2.Deleted = 0 + AND p.Deleted = 0 + FOR JSON PATH + ) AS ProvidersJson + FROM resources.ResourceVersionProvider rp + GROUP BY rp.ResourceVersionId +) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId + + JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0 + JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0 + JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 + JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 + JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 + JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 + LEFT JOIN hub.UserBookmark ub + ON ub.UserId = @UserId + AND ub.ResourceReferenceId = rrRef.OriginalResourceReferenceId + LEFT JOIN ( + SELECT DISTINCT CatalogueNodeId + FROM hub.RoleUserGroupView rug + JOIN hub.UserUserGroup uug + ON rug.UserGroupId = uug.UserGroupId + WHERE rug.ScopeTypeId = 1 + AND rug.RoleId IN (1,2,3) + AND uug.Deleted = 0 + AND uug.UserId = @UserId + ) auth ON n.Id = auth.CatalogueNodeId + LEFT JOIN resources.VideoResourceVersion vrv + ON vrv.ResourceVersionId = r.CurrentResourceVersionId + LEFT JOIN resources.AudioResourceVersion arv2 + ON arv2.ResourceVersionId = r.CurrentResourceVersionId + ORDER BY ma.ResourceActivityId DESC, rv.Title + OFFSET @OffsetRows ROWS + FETCH NEXT @FetchRows ROWS ONLY; + END diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql index 3c0186a08..ac69b8967 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql @@ -7,106 +7,176 @@ -- -- 24 Jun 2024 OA Initial Revision -- 27 Jun 2024 SA Removed unused temp tables --- 29 Sep 2025 SA Integarted providerid details +-- 29 Sep 2025 SA Integarted providerid details +-- 31 Mar 2026 OA TD-7057 Script Optimization ------------------------------------------------------------------------------- CREATE PROCEDURE [resources].[GetPopularDashboardResources] - @UserId INT, - @PageNumber INT = 1, - @TotalRecords INT OUTPUT + @UserId INT, + @PageNumber INT = 1, + @TotalRecords INT OUTPUT AS BEGIN - DECLARE @MaxPageNumber INT = 4 - - IF @PageNumber > 4 - BEGIN - SET @PageNumber = @MaxPageNumber - END - - DECLARE @FetchRows INT = 3 - DECLARE @MaxRows INT = @MaxPageNUmber * @FetchRows - DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows - - DECLARE @Resources TABLE (ResourceId [int] NOT NULL PRIMARY KEY, ResourceActivityCount [int] NOT NULL); - - - INSERT INTO @Resources - SELECT TOP (@MaxRows) ra.ResourceId - ,Count(ra.ResourceVersionId) ResourceActivityCount - FROM resources.Resource r - JOIN resources.ResourceVersion rv On rv.id = r.CurrentResourceVersionId AND rv.VersionStatusId = 2 - JOIN activity.ResourceActivity ra ON ra.ResourceId = r.Id - JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 - JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 - JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 - JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 - JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 - GROUP BY ra.ResourceId - ORDER BY ResourceActivityCount DESC - SELECT - tr.ResourceId - ,( SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0 - ) AS ResourceReferenceID - ,r.CurrentResourceVersionId AS ResourceVersionId - ,r.ResourceTypeId AS ResourceTypeId - ,rv.Title - ,rv.Description - ,CASE - WHEN r.ResourceTypeId = 7 THEN - (SELECT vrv.DurationInMilliseconds from [resources].[VideoResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId) - WHEN r.ResourceTypeId = 2 THEN - (SELECT vrv.DurationInMilliseconds from [resources].[AudioResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId) - ELSE - NULL - END AS DurationInMilliseconds - ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName - ,cnv.Url AS Url - ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,rvrs.AverageRating - ,rvrs.RatingCount - ,rpAgg.ProvidersJson - FROM @Resources tr - JOIN resources.Resource r ON r.id = tr.ResourceId - JOIN resources.resourceversion rv ON rv.ResourceId = r.Id AND rv.id = r.CurrentResourceVersionId AND rv.Deleted = 0 - LEFT JOIN ( + SET NOCOUNT ON; + + DECLARE @MaxPageNumber INT = 4; + DECLARE @FetchRows INT = 3; + + IF @PageNumber > @MaxPageNumber + SET @PageNumber = @MaxPageNumber; + + DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; + DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; + + + -- Temp table instead of table variable + CREATE TABLE #Resources ( + ResourceId INT NOT NULL PRIMARY KEY, + ResourceActivityCount INT NOT NULL + ); + + -- Populate popular resources (TOP @MaxRows) + + INSERT INTO #Resources (ResourceId, ResourceActivityCount) + SELECT TOP (@MaxRows) + ra.ResourceId, + COUNT(ra.ResourceVersionId) AS ResourceActivityCount + FROM resources.Resource r + JOIN resources.ResourceVersion rv + ON rv.Id = r.CurrentResourceVersionId + AND rv.VersionStatusId = 2 + JOIN activity.ResourceActivity ra + ON ra.ResourceId = r.Id + JOIN hierarchy.NodeResource nr + ON r.Id = nr.ResourceId AND nr.Deleted = 0 + JOIN hierarchy.Node n + ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 + JOIN hierarchy.NodePath np + ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 + JOIN hierarchy.NodeVersion nv + ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 + JOIN hierarchy.CatalogueNodeVersion cnv + ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 + GROUP BY ra.ResourceId + ORDER BY COUNT(ra.ResourceVersionId) DESC; + + -- Main result set with paging + + SELECT + tr.ResourceId, + rrRef.OriginalResourceReferenceId AS ResourceReferenceID, + r.CurrentResourceVersionId AS ResourceVersionId, + r.ResourceTypeId, + rv.Title, + rv.Description, + CASE + WHEN r.ResourceTypeId = 7 THEN vrv.DurationInMilliseconds + WHEN r.ResourceTypeId = 2 THEN arv2.DurationInMilliseconds + ELSE NULL + END AS DurationInMilliseconds, + CASE WHEN n.Id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName, + cnv.Url, + CASE WHEN n.Id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl, + cnv.RestrictedAccess, + CAST( + CASE + WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL + THEN 0 ELSE 1 + END AS BIT + ) AS HasAccess, + ub.Id AS BookMarkId, + CAST(ISNULL(ub.Deleted,1) ^ 1 AS BIT) AS IsBookmarked, + rvrs.AverageRating, + rvrs.RatingCount, + rpAgg.ProvidersJson + FROM #Resources tr + JOIN resources.Resource r + ON r.Id = tr.ResourceId + JOIN resources.ResourceVersion rv + ON rv.ResourceId = r.Id + AND rv.Id = r.CurrentResourceVersionId + AND rv.Deleted = 0 + + -- Deterministic ResourceReference lookup + OUTER APPLY ( + SELECT TOP 1 rr.OriginalResourceReferenceId + FROM resources.ResourceReference rr + JOIN hierarchy.NodePath np2 + ON np2.Id = rr.NodePathId + AND np2.NodeId = n.Id + AND np2.Deleted = 0 + WHERE rr.ResourceId = rv.ResourceId + AND rr.Deleted = 0 + ORDER BY rr.Id DESC + ) rrRef + + -- Provider JSON aggregation + LEFT JOIN ( SELECT rp.ResourceVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson + ( + SELECT + p.Id, + p.Name, + p.Description, + p.Logo + FROM resources.ResourceVersionProvider rp2 + JOIN hub.Provider p ON p.Id = rp2.ProviderId + WHERE rp2.ResourceVersionId = rp.ResourceVersionId + AND rp2.Deleted = 0 + AND p.Deleted = 0 + FOR JSON PATH + ) AS ProvidersJson FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE p.Deleted = 0 and rp.Deleted = 0 GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId - JOIN resources.ResourceVersionRatingSummary rvrs ON r.CurrentResourceVersionId = rvrs.ResourceVersionId AND rvrs.Deleted = 0 - JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 - JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 - JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 - JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 - JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.ResourceReferenceId = (SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0) - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE rv.VersionStatusId = 2 - ORDER BY tr.ResourceActivityCount DESC, rv.Title - OFFSET @OffsetRows ROWS - FETCH NEXT @FetchRows ROWS ONLY - - SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM @Resources +) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId + + JOIN resources.ResourceVersionRatingSummary rvrs + ON r.CurrentResourceVersionId = rvrs.ResourceVersionId + AND rvrs.Deleted = 0 + + JOIN hierarchy.NodeResource nr + ON r.Id = nr.ResourceId AND nr.Deleted = 0 + JOIN hierarchy.Node n + ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 + JOIN hierarchy.NodePath np + ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 + JOIN hierarchy.NodeVersion nv + ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 + JOIN hierarchy.CatalogueNodeVersion cnv + ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 + + -- Bookmark lookup using same ResourceReference + LEFT JOIN hub.UserBookmark ub + ON ub.UserId = @UserId + AND ub.ResourceReferenceId = rrRef.OriginalResourceReferenceId + + -- Access check + LEFT JOIN ( + SELECT DISTINCT CatalogueNodeId + FROM hub.RoleUserGroupView rug + JOIN hub.UserUserGroup uug + ON rug.UserGroupId = uug.UserGroupId + WHERE rug.ScopeTypeId = 1 + AND rug.RoleId IN (1,2,3) + AND uug.Deleted = 0 + AND uug.UserId = @UserId + ) auth ON n.Id = auth.CatalogueNodeId + + + LEFT JOIN resources.VideoResourceVersion vrv + ON vrv.ResourceVersionId = r.CurrentResourceVersionId + LEFT JOIN resources.AudioResourceVersion arv2 + ON arv2.ResourceVersionId = r.CurrentResourceVersionId + + WHERE rv.VersionStatusId = 2 + ORDER BY tr.ResourceActivityCount DESC, rv.Title + OFFSET @OffsetRows ROWS + FETCH NEXT @FetchRows ROWS ONLY; + + + + SELECT @TotalRecords = COUNT(*) FROM #Resources; END + diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql index b9c7969c6..dd16bb2bc 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql @@ -8,92 +8,168 @@ -- 24 Jun 2024 OA Initial Revision -- 27 Jun 2024 SA Removed unused temp tables -- 29 Sep 2025 SA Integrated the provider dertails +-- 31 Mar 2026 OA TD-7057 Script Optimization ------------------------------------------------------------------------------- CREATE PROCEDURE [resources].[GetRatedDashboardResources] - @UserId INT, - @PageNumber INT = 1, - @TotalRecords INT OUTPUT + @UserId INT, + @PageNumber INT = 1, + @TotalRecords INT OUTPUT AS BEGIN - DECLARE @MaxPageNumber INT = 4 - - IF @PageNumber > 4 - BEGIN - SET @PageNumber = @MaxPageNumber - END - - DECLARE @FetchRows INT = 3 - DECLARE @MaxRows INT = @MaxPageNUmber * @FetchRows - DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows - - SELECT TOP(@MaxRows) r.Id AS ResourceId - ,( SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0 - ) AS ResourceReferenceID - ,r.CurrentResourceVersionId AS ResourceVersionId - ,r.ResourceTypeId AS ResourceTypeId - ,rv.Title - ,rv.Description - ,CASE - WHEN r.ResourceTypeId = 7 THEN - (SELECT vrv.DurationInMilliseconds from [resources].[VideoResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId) - WHEN r.ResourceTypeId = 2 THEN - (SELECT vrv.DurationInMilliseconds from [resources].[AudioResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId) - ELSE - NULL - END AS DurationInMilliseconds - ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName - ,cnv.Url AS Url - ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,rvrs.AverageRating - ,rvrs.RatingCount - ,rpAgg.ProvidersJson - INTO #ratedresources - FROM Resources.Resource r - JOIN resources.resourceversion rv ON rv.ResourceId = r.Id AND rv.id = r.CurrentResourceVersionId AND rv.Deleted = 0 - LEFT JOIN ( - SELECT - rp.ResourceVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE p.Deleted = 0 and rp.Deleted = 0 - GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId - JOIN resources.ResourceVersionRatingSummary rvrs ON r.CurrentResourceVersionId = rvrs.ResourceVersionId AND rvrs.RatingCount > 0 - JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 - JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 - JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 - JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 - JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.ResourceReferenceId = (SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0) - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE rv.VersionStatusId = 2 - ORDER BY rvrs.AverageRating DESC, rvrs.RatingCount DESC, rv.Title - - SELECT rr.* FROM #ratedresources rr - ORDER BY rr.AverageRating DESC, rr.RatingCount DESC, rr.Title - OFFSET @OffsetRows ROWS - FETCH NEXT @FetchRows ROWS ONLY - - SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM #ratedresources + SET NOCOUNT ON; + DECLARE @MaxPageNumber INT = 4; + DECLARE @FetchRows INT = 3; + + IF @PageNumber > @MaxPageNumber + SET @PageNumber = @MaxPageNumber; + + DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; + DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; + + + CREATE TABLE #RatedResources ( + ResourceId INT, + ResourceReferenceID INT, + ResourceVersionId INT, + ResourceTypeId INT, + Title NVARCHAR(500), + Description NVARCHAR(MAX), + DurationInMilliseconds INT NULL, + CatalogueName NVARCHAR(255), + Url NVARCHAR(500), + BadgeUrl NVARCHAR(500), + RestrictedAccess BIT, + HasAccess BIT, + BookMarkId INT NULL, + IsBookmarked BIT, + AverageRating FLOAT, + RatingCount INT, + ProvidersJson NVARCHAR(MAX) + ); + + -- Insert TOP(@MaxRows) rated resources + INSERT INTO #RatedResources + SELECT TOP (@MaxRows) + r.Id AS ResourceId, + rrRef.OriginalResourceReferenceId AS ResourceReferenceID, + r.CurrentResourceVersionId AS ResourceVersionId, + r.ResourceTypeId, + rv.Title, + rv.Description, + + CASE + WHEN r.ResourceTypeId = 7 THEN vrv.DurationInMilliseconds + WHEN r.ResourceTypeId = 2 THEN arv2.DurationInMilliseconds + ELSE NULL + END AS DurationInMilliseconds, + + CASE WHEN n.Id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName, + cnv.Url, + CASE WHEN n.Id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl, + cnv.RestrictedAccess, + + CAST( + CASE + WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL + THEN 0 ELSE 1 + END AS BIT + ) AS HasAccess, + + ub.Id AS BookMarkId, + CAST(ISNULL(ub.Deleted,1) ^ 1 AS BIT) AS IsBookmarked, + + rvrs.AverageRating, + rvrs.RatingCount, + rpAgg.ProvidersJson + + FROM Resources.Resource r + JOIN resources.ResourceVersion rv + ON rv.ResourceId = r.Id + AND rv.Id = r.CurrentResourceVersionId + AND rv.Deleted = 0 + + JOIN resources.ResourceVersionRatingSummary rvrs + ON rvrs.ResourceVersionId = r.CurrentResourceVersionId + AND rvrs.RatingCount > 0 + + -- Deterministic ResourceReference lookup + OUTER APPLY ( + SELECT TOP 1 rr.OriginalResourceReferenceId + FROM resources.ResourceReference rr + JOIN hierarchy.NodePath np2 + ON np2.Id = rr.NodePathId + AND np2.NodeId = nr.NodeId + AND np2.Deleted = 0 + WHERE rr.ResourceId = rv.ResourceId + AND rr.Deleted = 0 + ORDER BY rr.Id DESC + ) rrRef + + -- Provider JSON aggregation + + LEFT JOIN ( + SELECT + rp.ResourceVersionId, + ( + SELECT + p.Id, + p.Name, + p.Description, + p.Logo + FROM resources.ResourceVersionProvider rp2 + JOIN hub.Provider p ON p.Id = rp2.ProviderId + WHERE rp2.ResourceVersionId = rp.ResourceVersionId + AND rp2.Deleted = 0 + AND p.Deleted = 0 + FOR JSON PATH + ) AS ProvidersJson + FROM resources.ResourceVersionProvider rp + GROUP BY rp.ResourceVersionId +) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId + + -- Hierarchy joins + JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 + JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 + JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 + JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 + JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 + + -- Bookmark lookup + LEFT JOIN hub.UserBookmark ub + ON ub.UserId = @UserId + AND ub.ResourceReferenceId = rrRef.OriginalResourceReferenceId + + -- Access check + LEFT JOIN ( + SELECT DISTINCT CatalogueNodeId + FROM hub.RoleUserGroupView rug + JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId + WHERE rug.ScopeTypeId = 1 + AND rug.RoleId IN (1,2,3) + AND uug.Deleted = 0 + AND uug.UserId = @UserId + ) auth ON n.Id = auth.CatalogueNodeId + + LEFT JOIN resources.VideoResourceVersion vrv + ON vrv.ResourceVersionId = r.CurrentResourceVersionId + + LEFT JOIN resources.AudioResourceVersion arv2 + ON arv2.ResourceVersionId = r.CurrentResourceVersionId + + WHERE rv.VersionStatusId = 2 + ORDER BY rvrs.AverageRating DESC, rvrs.RatingCount DESC, rv.Title; + + + SELECT * + FROM #RatedResources + ORDER BY AverageRating DESC, RatingCount DESC, Title + OFFSET @OffsetRows ROWS + FETCH NEXT @FetchRows ROWS ONLY; + + SELECT @TotalRecords = COUNT(*) FROM #RatedResources; + END + diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql index eedbe00e6..4050d3b0c 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql @@ -8,91 +8,180 @@ -- 24 Jun 2024 OA Initial Revision -- 27 Jun 2024 SA Removed unused temp tables -- 29 Sep 2025 SA Integrated the provider dertails +-- 31 Mar 2026 OA TD-7057 Script Optimization ------------------------------------------------------------------------------- CREATE PROCEDURE [resources].[GetRecentDashboardResources] - @UserId INT, - @PageNumber INT = 1, - @TotalRecords INT OUTPUT + @UserId INT, + @PageNumber INT = 1, + @TotalRecords INT OUTPUT AS BEGIN - DECLARE @MaxPageNumber INT = 4 - - IF @PageNumber > 4 - BEGIN - SET @PageNumber = @MaxPageNumber - END - - DECLARE @FetchRows INT = 3 - DECLARE @MaxRows INT = @MaxPageNUmber * @FetchRows - DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows - - SELECT TOP(@MaxRows) r.Id AS ResourceId - ,( SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0 - ) AS ResourceReferenceID - ,r.CurrentResourceVersionId AS ResourceVersionId - ,r.ResourceTypeId AS ResourceTypeId - ,rv.Title - ,rv.Description - ,CASE - WHEN r.ResourceTypeId = 7 THEN - (SELECT vrv.DurationInMilliseconds from [resources].[VideoResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId) - WHEN r.ResourceTypeId = 2 THEN - (SELECT vrv.DurationInMilliseconds from [resources].[AudioResourceVersion] vrv WHERE vrv.[ResourceVersionId] = r.CurrentResourceVersionId) - ELSE - NULL - END AS DurationInMilliseconds - ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.Name END AS CatalogueName - ,cnv.Url AS Url - ,CASE WHEN n.id = 1 THEN NULL ELSE cnv.BadgeUrl END AS BadgeUrl - ,cnv.RestrictedAccess - ,CAST(CASE WHEN cnv.RestrictedAccess = 1 AND auth.CatalogueNodeId IS NULL THEN 0 ELSE 1 END AS bit) AS HasAccess - ,ub.Id AS BookMarkId - ,CAST(ISNULL(ub.[Deleted], 1) ^ 1 AS BIT) AS IsBookmarked - ,rvrs.AverageRating - ,rvrs.RatingCount - ,rpAgg.ProvidersJson - INTO #recentresources - FROM Resources.Resource r - JOIN resources.resourceversion rv ON rv.ResourceId = r.Id AND rv.id = r.CurrentResourceVersionId AND rv.Deleted = 0 - LEFT JOIN ( - SELECT - rp.ResourceVersionId, - JSON_QUERY('[' + STRING_AGG( - '{"Id":' + CAST(p.Id AS NVARCHAR) + - ',"Name":"' + p.Name + '"' + - ',"Description":"' + p.Description + '"' + - ',"Logo":"' + ISNULL(p.Logo, '') + '"}', - ',') + ']') AS ProvidersJson - FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE p.Deleted = 0 and rp.Deleted = 0 - GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId - JOIN hierarchy.Publication p ON rv.PublicationId = p.Id AND p.Deleted = 0 - JOIN resources.ResourceVersionRatingSummary rvrs ON rv.Id = rvrs.ResourceVersionId AND rvrs.Deleted = 0 - JOIN hierarchy.NodeResource nr ON r.Id = nr.ResourceId AND nr.Deleted = 0 - JOIN hierarchy.Node n ON n.Id = nr.NodeId AND n.Hidden = 0 AND n.Deleted = 0 - JOIN hierarchy.NodePath np ON np.NodeId = n.Id AND np.Deleted = 0 AND np.IsActive = 1 - JOIN hierarchy.NodeVersion nv ON nv.NodeId = np.CatalogueNodeId AND nv.VersionStatusId = 2 AND nv.Deleted = 0 - JOIN hierarchy.CatalogueNodeVersion cnv ON cnv.NodeVersionId = nv.Id AND cnv.Deleted = 0 - LEFT JOIN hub.UserBookmark ub ON ub.UserId = @UserId AND ub.ResourceReferenceId = (SELECT TOP 1 rr.OriginalResourceReferenceId - FROM [resources].[ResourceReference] rr - JOIN hierarchy.NodePath np on np.id = rr.NodePathId and np.NodeId = n.Id and np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId AND rr.Deleted = 0) - LEFT JOIN ( SELECT DISTINCT CatalogueNodeId - FROM [hub].[RoleUserGroupView] rug JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId - WHERE rug.ScopeTypeId = 1 and rug.RoleId in (1,2,3) and uug.Deleted = 0 and uug.UserId = @userId) auth ON n.Id = auth.CatalogueNodeId - WHERE rv.VersionStatusId = 2 - ORDER BY p.CreateDate DESC - - SELECT rr.* FROM #recentresources rr - ORDER BY rr.ResourceVersionId DESC - OFFSET @OffsetRows ROWS - FETCH NEXT @FetchRows ROWS ONLY - - SELECT @TotalRecords = CASE WHEN COUNT(*) > 12 THEN @MaxRows ELSE COUNT(*) END FROM #recentresources + SET NOCOUNT ON; + + DECLARE @MaxPageNumber INT = 4; + DECLARE @FetchRows INT = 3; + + IF @PageNumber > @MaxPageNumber + SET @PageNumber = @MaxPageNumber; + + DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; + DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; + + -- Providers precomputed: + SELECT + rp.ResourceVersionId, + ProvidersJson = ( + SELECT + p.Id, + p.Name, + p.Description, + p.Logo + FROM resources.ResourceVersionProvider rp2 + JOIN hub.Provider p ON p.Id = rp2.ProviderId + WHERE rp2.ResourceVersionId = rp.ResourceVersionId + AND rp2.Deleted = 0 + AND p.Deleted = 0 + FOR JSON PATH + ) +INTO #Providers +FROM resources.ResourceVersionProvider rp +WHERE rp.Deleted = 0 +GROUP BY rp.ResourceVersionId; + + + -- Auth precomputed + + SELECT DISTINCT CatalogueNodeId + INTO #Auth + FROM hub.RoleUserGroupView rug + JOIN hub.UserUserGroup uug ON rug.UserGroupId = uug.UserGroupId + WHERE rug.ScopeTypeId = 1 + AND rug.RoleId IN (1,2,3) + AND uug.Deleted = 0 + AND uug.UserId = @UserId; + + -- ResourceReferenceId (precomputed) + SELECT + rr.ResourceId, + rr.NodePathId, + rr.OriginalResourceReferenceId + INTO #Ref + FROM resources.ResourceReference rr + JOIN hierarchy.NodePath np ON np.Id = rr.NodePathId + AND np.Deleted = 0 + WHERE rr.Deleted = 0; + + + SELECT TOP (@MaxRows) + r.Id AS ResourceId, + rv.Id AS ResourceVersionId, + r.ResourceTypeId, + rv.Title, + rv.Description, + rvrs.AverageRating, + rvrs.RatingCount, + p.CreateDate, + cnv.Name AS CatalogueName, + cnv.Url, + cnv.BadgeUrl, + cnv.RestrictedAccess, + prov.ProvidersJson, + + --------------------------------------------------------------------- + -- Duration (Video/Audio) + --------------------------------------------------------------------- + CASE + WHEN r.ResourceTypeId = 7 THEN + (SELECT DurationInMilliseconds + FROM resources.VideoResourceVersion + WHERE ResourceVersionId = rv.Id) + WHEN r.ResourceTypeId = 2 THEN + (SELECT DurationInMilliseconds + FROM resources.AudioResourceVersion + WHERE ResourceVersionId = rv.Id) + ELSE NULL + END AS DurationInMilliseconds, + + --------------------------------------------------------------------- + -- ResourceReferenceId (TOP 1 per original logic) + --------------------------------------------------------------------- + (SELECT TOP 1 rf.OriginalResourceReferenceId + FROM #Ref rf + JOIN hierarchy.NodePath np2 ON np2.Id = rf.NodePathId + AND np2.NodeId = n.Id + AND np2.Deleted = 0 + WHERE rf.ResourceId = r.Id) AS ResourceReferenceId, + + + -- Bookmark + + ub.Id AS BookMarkId, + CAST(ISNULL(ub.Deleted, 1) ^ 1 AS BIT) AS IsBookmarked, + + -- Access + + CAST( + CASE + WHEN cnv.RestrictedAccess = 1 + AND auth.CatalogueNodeId IS NULL + THEN 0 ELSE 1 + END AS BIT + ) AS HasAccess + + INTO #Recent + FROM resources.Resource r + JOIN resources.ResourceVersion rv + ON rv.Id = r.CurrentResourceVersionId + AND rv.Deleted = 0 + AND rv.VersionStatusId = 2 + JOIN hierarchy.Publication p + ON rv.PublicationId = p.Id + AND p.Deleted = 0 + JOIN resources.ResourceVersionRatingSummary rvrs + ON rv.Id = rvrs.ResourceVersionId + AND rvrs.Deleted = 0 + JOIN hierarchy.NodeResource nr + ON nr.ResourceId = r.Id + AND nr.Deleted = 0 + JOIN hierarchy.Node n + ON n.Id = nr.NodeId + AND n.Hidden = 0 + AND n.Deleted = 0 + JOIN hierarchy.NodePath np + ON np.NodeId = n.Id + AND np.Deleted = 0 + AND np.IsActive = 1 + JOIN hierarchy.NodeVersion nv + ON nv.NodeId = np.CatalogueNodeId + AND nv.VersionStatusId = 2 + AND nv.Deleted = 0 + JOIN hierarchy.CatalogueNodeVersion cnv + ON cnv.NodeVersionId = nv.Id + AND cnv.Deleted = 0 + LEFT JOIN #Providers prov + ON prov.ResourceVersionId = rv.Id + LEFT JOIN #Auth auth + ON auth.CatalogueNodeId = n.Id + LEFT JOIN hub.UserBookmark ub + ON ub.UserId = @UserId + AND ub.ResourceReferenceId = ( + SELECT TOP 1 rf.OriginalResourceReferenceId + FROM #Ref rf + JOIN hierarchy.NodePath np2 ON np2.Id = rf.NodePathId + AND np2.NodeId = n.Id + AND np2.Deleted = 0 + WHERE rf.ResourceId = r.Id + ) + ORDER BY p.CreateDate DESC; + + + SELECT @TotalRecords = COUNT(*) FROM #Recent; + + SELECT * + FROM #Recent + ORDER BY CreateDate DESC + OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; + END +