From 9948c98c0c7d95ea15d22ce90fc7013ea237f403 Mon Sep 17 00:00:00 2001 From: Tobi Awe Date: Tue, 31 Mar 2026 07:32:21 +0100 Subject: [PATCH 1/5] TD-7070 dashboard script optimization --- .../GetUserInProgressLearningActivities.sql | 133 +++-- .../Hierarchy/GetDashboardCatalogues.sql | 537 ++++++++---------- ...GetMyRecentCompletedDashboardResources.sql | 304 ++++++---- .../GetPopularDashboardResources.sql | 269 ++++++--- .../Resources/GetRatedDashboardResources.sql | 253 ++++++--- .../Resources/GetRecentDashboardResources.sql | 260 ++++++--- 6 files changed, 1013 insertions(+), 743 deletions(-) diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql index 12357075e..73026523f 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,80 @@ 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, - ( + 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 + + OUTER APPLY ( 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()) -) + FROM resources.ResourceReference rr + WHERE rr.ResourceId = rv.ResourceId + AND rr.Deleted = 0 + ORDER BY rr.Id DESC + ) rrRef + + 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) +) + 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/GetMyRecentCompletedDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql index 465cc84a3..b722269b7 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql @@ -8,128 +8,194 @@ -- 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].[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 ( - 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 - + 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 #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; + + -- logic: cap at @MaxRows (12) + SELECT @TotalRecords = + CASE WHEN COUNT(*) > @MaxRows THEN @MaxRows ELSE COUNT(*) END + FROM #MyActivity; + + 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 + + OUTER APPLY ( + SELECT TOP 1 rr.OriginalResourceReferenceId + FROM resources.ResourceReference rr + JOIN hierarchy.NodePath np + ON np.Id = rr.NodePathId + AND np.NodeId = nr.NodeId + AND np.Deleted = 0 + WHERE rr.ResourceId = rv.ResourceId + AND rr.Deleted = 0 + ORDER BY rr.Id DESC -- Ensures deterministic match with original SP + ) rrRef + + 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 rp.Deleted = 0 AND p.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 = 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..a12aa55ee 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql @@ -7,106 +7,187 @@ -- -- 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 - 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 + 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 + 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 + + 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 + + ---------------------------------------------------------------------- + -- Duration joins + ---------------------------------------------------------------------- + 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; + + ---------------------------------------------------------------------- + -- Total records (capped at 12) + ---------------------------------------------------------------------- + SELECT @TotalRecords = + CASE WHEN COUNT(*) > @MaxRows THEN @MaxRows ELSE COUNT(*) END + 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..4b94378b2 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql @@ -8,92 +8,183 @@ -- 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, + 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 rp.Deleted = 0 AND p.Deleted = 0 + 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 + + ---------------------------------------------------------------------- + -- Duration joins + ---------------------------------------------------------------------- + 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; + + ---------------------------------------------------------------------- + -- Final paged result + ---------------------------------------------------------------------- + SELECT * + FROM #RatedResources + ORDER BY AverageRating DESC, RatingCount DESC, Title + OFFSET @OffsetRows ROWS + FETCH NEXT @FetchRows ROWS ONLY; + + ---------------------------------------------------------------------- + -- Total records (capped at 12) + ---------------------------------------------------------------------- + SELECT @TotalRecords = + CASE WHEN COUNT(*) > @MaxRows THEN @MaxRows ELSE COUNT(*) END + 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..11b1e7c17 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql @@ -8,91 +8,187 @@ -- 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 (pre-aggregated) + ------------------------------------------------------------------------- + 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 + INTO #Providers + FROM resources.ResourceVersionProvider rp + JOIN hub.Provider p ON p.Id = rp.ProviderId + WHERE rp.Deleted = 0 AND p.Deleted = 0 + GROUP BY rp.ResourceVersionId; + + ------------------------------------------------------------------------- + -- User Authorization (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; + + ------------------------------------------------------------------------- + -- Main dataset (TOP @MaxRows) + ------------------------------------------------------------------------- + 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; + + ------------------------------------------------------------------------- + -- Total Records + ------------------------------------------------------------------------- + SELECT @TotalRecords = CASE WHEN COUNT(*) > @MaxRows THEN @MaxRows ELSE COUNT(*) END + FROM #Recent; + + ------------------------------------------------------------------------- + -- Final Paged Output + ------------------------------------------------------------------------- + SELECT * + FROM #Recent + ORDER BY CreateDate DESC + OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; + END +GO From 4c7c01990467878aefa15f3bbb5122de26478987 Mon Sep 17 00:00:00 2001 From: Tobi Awe Date: Wed, 1 Apr 2026 09:27:30 +0100 Subject: [PATCH 2/5] . --- ...GetMyRecentCompletedDashboardResources.sql | 149 +++++++++++++----- .../Resources/GetRecentDashboardResources.sql | 2 +- 2 files changed, 109 insertions(+), 42 deletions(-) diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql index b722269b7..066e5e5fb 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql @@ -5,16 +5,16 @@ -- -- 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 SET NOCOUNT ON; @@ -25,7 +25,7 @@ BEGIN IF @PageNumber > @MaxPageNumber SET @PageNumber = @MaxPageNumber; - DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; + DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; -- 12 DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; CREATE TABLE #MyActivity ( @@ -49,13 +49,15 @@ BEGIN FROM activity.ResourceActivity a WHERE a.UserId = @UserId ) - INSERT INTO #MyActivity + INSERT INTO #MyActivity (ResourceId, ResourceActivityId) 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 + 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 @@ -95,11 +97,49 @@ BEGIN ) ORDER BY la.Id DESC; - -- logic: cap at @MaxRows (12) + ------------------------------------------------------------------------- + -- TotalRecords: must match original SP EXACTLY (join-based count) + ------------------------------------------------------------------------- SELECT @TotalRecords = CASE WHEN COUNT(*) > @MaxRows THEN @MaxRows ELSE COUNT(*) END - FROM #MyActivity; + 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 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 + ) + 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; + ------------------------------------------------------------------------- + -- Main result set + ------------------------------------------------------------------------- SELECT r.Id AS ResourceId, rrRef.OriginalResourceReferenceId AS ResourceReferenceID, @@ -134,21 +174,13 @@ BEGIN 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 - - OUTER APPLY ( - SELECT TOP 1 rr.OriginalResourceReferenceId - FROM resources.ResourceReference rr - JOIN hierarchy.NodePath np - ON np.Id = rr.NodePathId - AND np.NodeId = nr.NodeId - AND np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId - AND rr.Deleted = 0 - ORDER BY rr.Id DESC -- Ensures deterministic match with original SP - ) rrRef + 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 @@ -160,22 +192,56 @@ BEGIN ',"Logo":"' + ISNULL(p.Logo,'') + '"}', ',') + ']') AS ProvidersJson FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE rp.Deleted = 0 AND p.Deleted = 0 + JOIN hub.Provider p + ON p.Id = rp.ProviderId + WHERE rp.Deleted = 0 + AND p.Deleted = 0 GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId + ) 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 + 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 + + -- OUTER APPLY must come after nr is available + 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 + + 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 + AND ub.ResourceReferenceId = rrRef.OriginalResourceReferenceId LEFT JOIN ( SELECT DISTINCT CatalogueNodeId @@ -183,10 +249,11 @@ BEGIN 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 + 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 diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql index 11b1e7c17..d308fcc43 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql @@ -191,4 +191,4 @@ BEGIN OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; END -GO + From 8816e276540214838346b1add4e40bed733e9998 Mon Sep 17 00:00:00 2001 From: Tobi Awe Date: Wed, 1 Apr 2026 12:07:51 +0100 Subject: [PATCH 3/5] update --- ...GetMyRecentCompletedDashboardResources.sql | 176 +++++------------- .../Resources/GetRecentDashboardResources.sql | 32 +--- 2 files changed, 53 insertions(+), 155 deletions(-) diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql index 066e5e5fb..b9011c0ab 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql @@ -5,35 +5,33 @@ -- -- 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 details +-- 29 Sep 2025 SA Integrated the provider dertails -- 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 SET NOCOUNT ON; - DECLARE @MaxPageNumber INT = 4; DECLARE @FetchRows INT = 3; - IF @PageNumber > @MaxPageNumber SET @PageNumber = @MaxPageNumber; - - DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; -- 12 + DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; - + -- Use temp table instead of table variable: CREATE TABLE #MyActivity ( ResourceId INT PRIMARY KEY, ResourceActivityId INT ); - - -- Latest activity per resource per user + -- Latest activity per resource per user (we should consider precomputing this) WITH LatestActivity AS ( SELECT a.Id, @@ -49,15 +47,13 @@ BEGIN FROM activity.ResourceActivity a WHERE a.UserId = @UserId ) - INSERT INTO #MyActivity (ResourceId, ResourceActivityId) + 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 + 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 @@ -96,50 +92,8 @@ BEGIN ) ) ORDER BY la.Id DESC; - - ------------------------------------------------------------------------- - -- TotalRecords: must match original SP EXACTLY (join-based count) - ------------------------------------------------------------------------- - SELECT @TotalRecords = - CASE WHEN COUNT(*) > @MaxRows 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 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 - ) - 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; - - ------------------------------------------------------------------------- - -- Main result set - ------------------------------------------------------------------------- + -- Total count without duplicate queries: + SELECT @TotalRecords = COUNT(*) FROM #MyActivity; SELECT r.Id AS ResourceId, rrRef.OriginalResourceReferenceId AS ResourceReferenceID, @@ -147,41 +101,41 @@ BEGIN 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 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 + -- Replace correlated subquery with OUTER APPLY + OUTER APPLY ( + SELECT TOP 1 rr.OriginalResourceReferenceId + FROM resources.ResourceReference rr + JOIN hierarchy.NodePath np + ON np.Id = rr.NodePathId + AND np.NodeId = nr.NodeId + AND np.Deleted = 0 + WHERE rr.ResourceId = rv.ResourceId + AND rr.Deleted = 0 + ) rrRef LEFT JOIN ( SELECT rp.ResourceVersionId, @@ -192,77 +146,35 @@ BEGIN ',"Logo":"' + ISNULL(p.Logo,'') + '"}', ',') + ']') AS ProvidersJson FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p - ON p.Id = rp.ProviderId - WHERE rp.Deleted = 0 - AND p.Deleted = 0 + JOIN hub.Provider p ON p.Id = rp.ProviderId + WHERE rp.Deleted = 0 AND p.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 - - -- OUTER APPLY must come after nr is available - 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 - - 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 - + ) 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 = rrRef.OriginalResourceReferenceId - + 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 - + 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/GetRecentDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql index d308fcc43..8f6ef6623 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql @@ -28,9 +28,7 @@ BEGIN DECLARE @MaxRows INT = @MaxPageNumber * @FetchRows; DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; - ------------------------------------------------------------------------- - -- Providers (pre-aggregated) - ------------------------------------------------------------------------- + -- Providers precomputed: SELECT rp.ResourceVersionId, JSON_QUERY('[' + STRING_AGG( @@ -45,9 +43,8 @@ BEGIN WHERE rp.Deleted = 0 AND p.Deleted = 0 GROUP BY rp.ResourceVersionId; - ------------------------------------------------------------------------- - -- User Authorization (precomputed) - ------------------------------------------------------------------------- + -- Auth precomputed + SELECT DISTINCT CatalogueNodeId INTO #Auth FROM hub.RoleUserGroupView rug @@ -57,9 +54,7 @@ BEGIN AND uug.Deleted = 0 AND uug.UserId = @UserId; - ------------------------------------------------------------------------- -- ResourceReferenceId (precomputed) - ------------------------------------------------------------------------- SELECT rr.ResourceId, rr.NodePathId, @@ -70,9 +65,7 @@ BEGIN AND np.Deleted = 0 WHERE rr.Deleted = 0; - ------------------------------------------------------------------------- - -- Main dataset (TOP @MaxRows) - ------------------------------------------------------------------------- + SELECT TOP (@MaxRows) r.Id AS ResourceId, rv.Id AS ResourceVersionId, @@ -113,15 +106,14 @@ BEGIN 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 @@ -176,15 +168,9 @@ BEGIN ) ORDER BY p.CreateDate DESC; - ------------------------------------------------------------------------- - -- Total Records - ------------------------------------------------------------------------- - SELECT @TotalRecords = CASE WHEN COUNT(*) > @MaxRows THEN @MaxRows ELSE COUNT(*) END - FROM #Recent; - ------------------------------------------------------------------------- - -- Final Paged Output - ------------------------------------------------------------------------- + SELECT @TotalRecords = COUNT(*) FROM #Recent; + SELECT * FROM #Recent ORDER BY CreateDate DESC From 228ee4d3f9347dcdfb396a6e48977684500cc446 Mon Sep 17 00:00:00 2001 From: Tobi Awe Date: Wed, 1 Apr 2026 12:23:36 +0100 Subject: [PATCH 4/5] comment cleanup --- .../GetPopularDashboardResources.sql | 30 +++++-------------- .../Resources/GetRatedDashboardResources.sql | 28 +++-------------- 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql index a12aa55ee..b0b23b444 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql @@ -28,17 +28,15 @@ BEGIN 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, @@ -62,9 +60,8 @@ BEGIN GROUP BY ra.ResourceId ORDER BY COUNT(ra.ResourceVersionId) DESC; - ---------------------------------------------------------------------- -- Main result set with paging - ---------------------------------------------------------------------- + SELECT tr.ResourceId, rrRef.OriginalResourceReferenceId AS ResourceReferenceID, @@ -100,9 +97,7 @@ BEGIN AND rv.Id = r.CurrentResourceVersionId AND rv.Deleted = 0 - ---------------------------------------------------------------------- -- Deterministic ResourceReference lookup - ---------------------------------------------------------------------- OUTER APPLY ( SELECT TOP 1 rr.OriginalResourceReferenceId FROM resources.ResourceReference rr @@ -115,9 +110,7 @@ BEGIN ORDER BY rr.Id DESC ) rrRef - ---------------------------------------------------------------------- -- Provider JSON aggregation - ---------------------------------------------------------------------- LEFT JOIN ( SELECT rp.ResourceVersionId, @@ -148,16 +141,12 @@ BEGIN 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 @@ -169,9 +158,7 @@ BEGIN AND uug.UserId = @UserId ) auth ON n.Id = auth.CatalogueNodeId - ---------------------------------------------------------------------- - -- Duration joins - ---------------------------------------------------------------------- + LEFT JOIN resources.VideoResourceVersion vrv ON vrv.ResourceVersionId = r.CurrentResourceVersionId LEFT JOIN resources.AudioResourceVersion arv2 @@ -182,12 +169,9 @@ BEGIN OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; - ---------------------------------------------------------------------- - -- Total records (capped at 12) - ---------------------------------------------------------------------- - SELECT @TotalRecords = - CASE WHEN COUNT(*) > @MaxRows THEN @MaxRows ELSE COUNT(*) END - FROM #Resources; + + + 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 4b94378b2..6be86f698 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql @@ -49,9 +49,7 @@ BEGIN ProvidersJson NVARCHAR(MAX) ); - ---------------------------------------------------------------------- -- Insert TOP(@MaxRows) rated resources - ---------------------------------------------------------------------- INSERT INTO #RatedResources SELECT TOP (@MaxRows) r.Id AS ResourceId, @@ -96,9 +94,7 @@ BEGIN ON rvrs.ResourceVersionId = r.CurrentResourceVersionId AND rvrs.RatingCount > 0 - ---------------------------------------------------------------------- -- Deterministic ResourceReference lookup - ---------------------------------------------------------------------- OUTER APPLY ( SELECT TOP 1 rr.OriginalResourceReferenceId FROM resources.ResourceReference rr @@ -111,9 +107,8 @@ BEGIN ORDER BY rr.Id DESC ) rrRef - ---------------------------------------------------------------------- -- Provider JSON aggregation - ---------------------------------------------------------------------- + LEFT JOIN ( SELECT rp.ResourceVersionId, @@ -129,25 +124,19 @@ BEGIN 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 @@ -158,9 +147,6 @@ BEGIN AND uug.UserId = @UserId ) auth ON n.Id = auth.CatalogueNodeId - ---------------------------------------------------------------------- - -- Duration joins - ---------------------------------------------------------------------- LEFT JOIN resources.VideoResourceVersion vrv ON vrv.ResourceVersionId = r.CurrentResourceVersionId @@ -170,21 +156,15 @@ BEGIN WHERE rv.VersionStatusId = 2 ORDER BY rvrs.AverageRating DESC, rvrs.RatingCount DESC, rv.Title; - ---------------------------------------------------------------------- - -- Final paged result - ---------------------------------------------------------------------- + SELECT * FROM #RatedResources ORDER BY AverageRating DESC, RatingCount DESC, Title OFFSET @OffsetRows ROWS FETCH NEXT @FetchRows ROWS ONLY; - ---------------------------------------------------------------------- - -- Total records (capped at 12) - ---------------------------------------------------------------------- - SELECT @TotalRecords = - CASE WHEN COUNT(*) > @MaxRows THEN @MaxRows ELSE COUNT(*) END - FROM #RatedResources; + SELECT @TotalRecords = COUNT(*) FROM #RatedResources; + END From 8af1a1504c6b28c4c7ba283645ba1c81919b5cc7 Mon Sep 17 00:00:00 2001 From: Tobi Awe Date: Wed, 1 Apr 2026 16:32:25 +0100 Subject: [PATCH 5/5] JSON_QUERY and OUTER APPLY replacement --- .../GetUserInProgressLearningActivities.sql | 42 ++++--- .../GetMyInProgressDashboardResources.sql | 31 ++--- ...LearningCertificatesDashboardResources.sql | 31 ++--- ...GetMyRecentCompletedDashboardResources.sql | 109 +++++++++++++----- .../GetPopularDashboardResources.sql | 31 ++--- .../Resources/GetRatedDashboardResources.sql | 33 +++--- .../Resources/GetRecentDashboardResources.sql | 33 +++--- 7 files changed, 198 insertions(+), 112 deletions(-) diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql index 73026523f..a4c82c855 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetUserInProgressLearningActivities.sql @@ -56,29 +56,39 @@ BEGIN INNER JOIN resources.Resource r ON ra.ResourceId = r.Id INNER JOIN resources.ResourceVersion rv ON rv.Id = ra.ResourceVersionId AND rv.Deleted = 0 - OUTER APPLY ( - SELECT TOP 1 rr.OriginalResourceReferenceId - FROM resources.ResourceReference rr - WHERE rr.ResourceId = rv.ResourceId - AND rr.Deleted = 0 - ORDER BY rr.Id DESC - ) rrRef + 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 ( + 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 + 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 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 b9011c0ab..787171f56 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetMyRecentCompletedDashboardResources.sql @@ -5,9 +5,9 @@ -- -- 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 ------------------------------------------------------------------------------- @@ -20,18 +20,25 @@ CREATE PROCEDURE [resources].[GetMyRecentCompletedDashboardResources] AS BEGIN 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; - -- Use temp table instead of table variable: + + -- Temp table for activity CREATE TABLE #MyActivity ( ResourceId INT PRIMARY KEY, ResourceActivityId INT ); - -- Latest activity per resource per user (we should consider precomputing this) + + + -- Latest activity per resource per user + WITH LatestActivity AS ( SELECT a.Id, @@ -92,8 +99,48 @@ BEGIN ) ) ORDER BY la.Id DESC; - -- Total count without duplicate queries: + + + -- 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 + 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, @@ -125,34 +172,35 @@ BEGIN 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 - -- Replace correlated subquery with OUTER APPLY - OUTER APPLY ( - SELECT TOP 1 rr.OriginalResourceReferenceId - FROM resources.ResourceReference rr - JOIN hierarchy.NodePath np - ON np.Id = rr.NodePathId - AND np.NodeId = nr.NodeId - AND np.Deleted = 0 - WHERE rr.ResourceId = rv.ResourceId - AND rr.Deleted = 0 - ) rrRef + 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, - 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 rp.Deleted = 0 AND p.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 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 @@ -177,4 +225,5 @@ BEGIN 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 b0b23b444..ac69b8967 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetPopularDashboardResources.sql @@ -112,19 +112,24 @@ BEGIN -- 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 - 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 resources.ResourceVersionRatingSummary rvrs ON r.CurrentResourceVersionId = rvrs.ResourceVersionId diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql index 6be86f698..dd16bb2bc 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRatedDashboardResources.sql @@ -109,20 +109,25 @@ BEGIN -- 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 - FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE rp.Deleted = 0 AND p.Deleted = 0 - GROUP BY rp.ResourceVersionId - ) rpAgg ON rpAgg.ResourceVersionId = r.CurrentResourceVersionId + 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 diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql index 8f6ef6623..4050d3b0c 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Resources/GetRecentDashboardResources.sql @@ -29,19 +29,26 @@ BEGIN DECLARE @OffsetRows INT = (@PageNumber - 1) * @FetchRows; -- Providers precomputed: - 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 - INTO #Providers - FROM resources.ResourceVersionProvider rp - JOIN hub.Provider p ON p.Id = rp.ProviderId - WHERE rp.Deleted = 0 AND p.Deleted = 0 - GROUP BY rp.ResourceVersionId; + 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