diff --git a/DigitalLearningSolutions.Data.Migrations/202607300908_AddIndexesForGetAllDelegates.cs b/DigitalLearningSolutions.Data.Migrations/202607300908_AddIndexesForGetAllDelegates.cs new file mode 100644 index 0000000000..d9e056b31a --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202607300908_AddIndexesForGetAllDelegates.cs @@ -0,0 +1,43 @@ +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + using FluentMigrator.SqlServer; + + [Migration(202607300908)] + public class AddIndexesForGetAllDelegates : Migration + { + public override void Up() + { + Create.Index("IX_AdminAccounts_Active_UserID_CentreID_ID").OnTable("AdminAccounts") + .OnColumn("Active").Ascending() + .OnColumn("UserID").Ascending() + .OnColumn("CentreID").Ascending() + .WithOptions().NonClustered() + .Include("ID"); + + Create.Index("IX_DelegateAccounts_CentreID_Active_Approved_RegistrationConfirmationHash_UserID").OnTable("DelegateAccounts") + .OnColumn("CentreID").Ascending() + .OnColumn("Active").Ascending() + .OnColumn("Approved").Ascending() + .OnColumn("RegistrationConfirmationHash").Ascending() + .OnColumn("UserID").Ascending() + .WithOptions().NonClustered() + .Include("ID") + .Include("CandidateNumber") + .Include("DateRegistered") + .Include("LastAccessed") + .Include("SelfReg"); + + Create.Index("IX_Users_LearningHubAuthID").OnTable("Users") + .OnColumn("LearningHubAuthID").Ascending() + .WithOptions().NonClustered(); + } + + public override void Down() + { + Delete.Index("IX_AdminAccounts_Active_UserID_CentreID_ID").OnTable("AdminAccounts"); + Delete.Index("IX_DelegateAccounts_CentreID_Active_Approved_RegistrationConfirmationHash_UserID").OnTable("DelegateAccounts"); + Delete.Index("IX_Users_LearningHubAuthID").OnTable("Users"); + } + } +} diff --git a/DigitalLearningSolutions.Data/DataServices/CentresDataService.cs b/DigitalLearningSolutions.Data/DataServices/CentresDataService.cs index e539adb012..8d8d499d94 100644 --- a/DigitalLearningSolutions.Data/DataServices/CentresDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CentresDataService.cs @@ -280,10 +280,7 @@ FROM Centres AS c int contractType, string centreStatus) { - if (!string.IsNullOrEmpty(search)) - { - search = search.Trim(); - } + search = string.IsNullOrWhiteSpace(search) ? string.Empty : search.Trim(); string sql = @"SELECT c.CentreID, c.CentreName, c.ContactForename, @@ -298,10 +295,10 @@ FROM Centres AS c FROM Centres AS c INNER JOIN Regions AS r ON r.RegionID = c.RegionID INNER JOIN CentreTypes AS ct ON ct.CentreTypeId = c.CentreTypeId - WHERE c.CentreName LIKE N'%' + @search + N'%' - AND ((c.RegionID = @region) OR (@region = 0)) AND ((c.CentreTypeId = @centreType) OR (@centreType = 0)) - AND ((c.ContractTypeID = @contractType) OR (@contractType = 0)) AND ((@centreStatus = 'Any') OR (@centreStatus = 'Active' AND c.Active = 1) OR (@centreStatus = 'Inactive' AND c.Active = 0)) - ORDER BY LTRIM(c.CentreName) + WHERE (@search = '' OR c.CentreName LIKE N'%' + @search + N'%') + AND (@region = 0 OR c.RegionID = @region) AND (@centreType = 0 OR c.CentreTypeId = @centreType) + AND (@contractType = 0 OR c.ContractTypeID = @contractType) AND ((@centreStatus = 'Any') OR (@centreStatus = 'Active' AND c.Active = 1) OR (@centreStatus = 'Inactive' AND c.Active = 0)) + ORDER BY c.CentreName OFFSET @offset ROWS FETCH NEXT @rows ROWS ONLY"; IEnumerable centreEntity = connection.Query( @@ -314,11 +311,11 @@ OFFSET @offset ROWS commandTimeout: 3000 ); int resultCount = connection.ExecuteScalar( - @$"SELECT COUNT(*) AS Matches + @"SELECT COUNT(*) AS Matches FROM Centres AS c - INNER JOIN Regions AS r ON r.RegionID = c.RegionID - INNER JOIN CentreTypes AS ct ON ct.CentreTypeId = c.CentreTypeId - WHERE c.CentreName LIKE N'%' + @search + N'%' AND ((c.RegionID = @region) OR (@region = 0)) AND ((c.CentreTypeId = @centreType) OR (@centreType = 0)) AND ((c.ContractTypeID = @contractType) OR (@contractType = 0)) AND ((@centreStatus = 'Any') OR (@centreStatus = 'Active' AND c.Active = 1) OR (@centreStatus = 'Inactive' AND c.Active = 0))", + WHERE (@search = '' OR c.CentreName LIKE N'%' + @search + N'%') + AND (@region = 0 OR c.RegionID = @region) AND (@centreType = 0 OR c.CentreTypeId = @centreType) + AND (@contractType = 0 OR c.ContractTypeID = @contractType) AND ((@centreStatus = 'Any') OR (@centreStatus = 'Active' AND c.Active = 1) OR (@centreStatus = 'Inactive' AND c.Active = 0))", new { search, region, centreType, contractType, centreStatus }, commandTimeout: 3000 ); @@ -742,8 +739,7 @@ public void ReactivateCentre(int centreId) CustomCourses AS RoleLimitCustomCourses, Trainers AS RoleLimitTrainers FROM Centres - WHERE (CentreId = @centreId) AND (Active = 1) - ORDER BY CentreName", + WHERE (CentreId = @centreId) AND (Active = 1)", new { centreId } ); } @@ -844,12 +840,13 @@ ROLLBACK TRANSACTION public int ResultCount(string search, int region, int centreType, int contractType, string centreStatus) { + search = string.IsNullOrWhiteSpace(search) ? string.Empty : search.Trim(); int resultCount = connection.ExecuteScalar( - @$"SELECT COUNT(*) AS Matches + @"SELECT COUNT(*) AS Matches FROM Centres AS c - INNER JOIN Regions AS r ON r.RegionID = c.RegionID - INNER JOIN CentreTypes AS ct ON ct.CentreTypeId = c.CentreTypeId - WHERE c.CentreName LIKE N'%' + @search + N'%' AND ((c.RegionID = @region) OR (@region = 0)) AND ((c.CentreTypeId = @centreType) OR (@centreType = 0)) AND ((c.ContractTypeID = @contractType) OR (@contractType = 0)) AND ((@centreStatus = 'Any') OR (@centreStatus = 'Active' AND c.Active = 1) OR (@centreStatus = 'Inactive' AND c.Active = 0))", + WHERE (@search = '' OR c.CentreName LIKE N'%' + @search + N'%') + AND (@region = 0 OR c.RegionID = @region) AND (@centreType = 0 OR c.CentreTypeId = @centreType) + AND (@contractType = 0 OR c.ContractTypeID = @contractType) AND ((@centreStatus = 'Any') OR (@centreStatus = 'Active' AND c.Active = 1) OR (@centreStatus = 'Inactive' AND c.Active = 0))", new { search, region, centreType, contractType, centreStatus }, commandTimeout: 3000 ); @@ -859,55 +856,80 @@ FROM Centres AS c public IEnumerable GetAllCentresForSuperAdminExport(string search, int region, int centreType, int contractType, string centreStatus, int exportQueryRowLimit, int currentRun) { - if (!string.IsNullOrEmpty(search)) - { - search = search.Trim(); - } + search = string.IsNullOrWhiteSpace(search) ? string.Empty : search.Trim(); string sql = @"SELECT - CentreID, - Active, - CentreName, - ContactSurname + ', ' + ContactForename AS Contact, ContactEmail, - ContactTelephone, - (SELECT RegionName FROM Regions WHERE (RegionID = c.RegionID)) AS RegionName, - (SELECT CentreType FROM CentreTypes - WHERE (CentreTypeID = c.CentreTypeID)) AS CentreType, - IPPrefix, - CentreCreated, - (SELECT COUNT(*) AS Expr1 FROM Candidates - WHERE (CentreID = c.CentreID)) AS Delegates, - (SELECT COUNT(Progress.ProgressID) AS Registrations - FROM Progress - INNER JOIN Candidates AS Candidates_1 ON Progress.CandidateID = Candidates_1.CandidateID - WHERE (Candidates_1.CentreID = c.CentreID)) AS CourseEnrolments, - (SELECT COUNT(Progress_1.ProgressID) AS Completions - FROM Progress AS Progress_1 - INNER JOIN - Candidates AS Candidates_1 ON Progress_1.CandidateID = Candidates_1.CandidateID - WHERE (Progress_1.Completed IS NOT NULL) AND (Candidates_1.CentreID = c.CentreID)) AS CourseCompletions, - (SELECT SUM(e.Duration) AS Expr1 - FROM Sessions AS e INNER JOIN - Candidates AS Candidates_2 ON e.CandidateID = Candidates_2.CandidateID - WHERE (Candidates_2.CentreID = c.CentreID)) / 60 AS LearningHours, - (SELECT COUNT(*) AS Expr1 - FROM AdminUsers - WHERE (CentreID = c.CentreID)) AS AdminUsers, - (SELECT MAX(ADS.LoginTime) AS Expr1 - FROM AdminSessions AS ADS INNER JOIN - AdminUsers AS ADU ON ADS.AdminID = ADU.AdminID - WHERE (ADU.CentreID = c.CentreID)) AS LastAdminLogin, - (SELECT MAX(Sessions.LoginTime) AS Expr1 - FROM Sessions INNER JOIN - Customisations ON Sessions.CustomisationID = Customisations.CustomisationID - WHERE (Customisations.CentreID = c.CentreID)) AS LastLearnerLogin, - (SELECT ContractType FROM ContractTypes WHERE ContractTypeID = c.ContractTypeID) AS ContractType, - CCLicences, ServerSpaceBytes, - ServerSpaceUsed + c.CentreID, + c.Active, + c.CentreName, + c.ContactSurname + ', ' + c.ContactForename AS Contact, + c.ContactEmail, + c.ContactTelephone, + r.RegionName, + ct.CentreType, + c.IPPrefix, + c.CentreCreated, + ISNULL(d.Delegates, 0) AS Delegates, + ISNULL(e.CourseEnrolments, 0) AS CourseEnrolments, + ISNULL(co.CourseCompletions, 0) AS CourseCompletions, + ISNULL(l.LearningHours, 0) AS LearningHours, + ISNULL(a.AdminUsers, 0) AS AdminUsers, + al.LastAdminLogin, + ll.LastLearnerLogin, + con.ContractType, + c.CCLicences, + c.ServerSpaceBytes, + c.ServerSpaceUsed FROM Centres AS c - WHERE c.CentreName LIKE N'%' + @search + N'%' - AND ((c.RegionID = @region) OR (@region = 0)) AND ((c.CentreTypeId = @centreType) OR (@centreType = 0)) - AND ((c.ContractTypeID = @contractType) OR (@contractType = 0)) AND ((@centreStatus = 'Any') OR (@centreStatus = 'Active' AND c.Active = 1) OR (@centreStatus = 'Inactive' AND c.Active = 0)) - ORDER BY LTRIM(c.CentreName) + INNER JOIN Regions AS r ON r.RegionID = c.RegionID + INNER JOIN CentreTypes AS ct ON ct.CentreTypeID = c.CentreTypeID + INNER JOIN ContractTypes AS con ON con.ContractTypeID = c.ContractTypeID + LEFT JOIN ( + SELECT CentreID, COUNT(*) AS Delegates + FROM Candidates + GROUP BY CentreID + ) AS d ON d.CentreID = c.CentreID + LEFT JOIN ( + SELECT ca.CentreID, COUNT(p.ProgressID) AS CourseEnrolments + FROM Progress AS p + INNER JOIN Candidates AS ca ON p.CandidateID = ca.CandidateID + GROUP BY ca.CentreID + ) AS e ON e.CentreID = c.CentreID + LEFT JOIN ( + SELECT ca.CentreID, COUNT(p.ProgressID) AS CourseCompletions + FROM Progress AS p + INNER JOIN Candidates AS ca ON p.CandidateID = ca.CandidateID + WHERE p.Completed IS NOT NULL + GROUP BY ca.CentreID + ) AS co ON co.CentreID = c.CentreID + LEFT JOIN ( + SELECT ca.CentreID, SUM(s.Duration) / 60 AS LearningHours + FROM Sessions AS s + INNER JOIN Candidates AS ca ON s.CandidateID = ca.CandidateID + GROUP BY ca.CentreID + ) AS l ON l.CentreID = c.CentreID + LEFT JOIN ( + SELECT CentreID, COUNT(*) AS AdminUsers + FROM AdminUsers + GROUP BY CentreID + ) AS a ON a.CentreID = c.CentreID + LEFT JOIN ( + SELECT au.CentreID, MAX(ads.LoginTime) AS LastAdminLogin + FROM AdminSessions AS ads + INNER JOIN AdminUsers AS au ON ads.AdminID = au.AdminID + GROUP BY au.CentreID + ) AS al ON al.CentreID = c.CentreID + LEFT JOIN ( + SELECT ca.CentreID, MAX(s.LoginTime) AS LastLearnerLogin + FROM Sessions AS s + INNER JOIN Customisations AS cu ON s.CustomisationID = cu.CustomisationID + INNER JOIN Candidates AS ca ON s.CandidateID = ca.CandidateID + WHERE cu.CentreID = ca.CentreID + GROUP BY ca.CentreID + ) AS ll ON ll.CentreID = c.CentreID + WHERE (@search = '' OR c.CentreName LIKE N'%' + @search + N'%') + AND (@region = 0 OR c.RegionID = @region) AND (@centreType = 0 OR c.CentreTypeId = @centreType) + AND (@contractType = 0 OR c.ContractTypeID = @contractType) AND ((@centreStatus = 'Any') OR (@centreStatus = 'Active' AND c.Active = 1) OR (@centreStatus = 'Inactive' AND c.Active = 0)) + ORDER BY c.CentreName OFFSET @exportQueryRowLimit * (@currentRun - 1) ROWS FETCH NEXT @exportQueryRowLimit ROWS ONLY"; IEnumerable centres = connection.Query( diff --git a/DigitalLearningSolutions.Data/DataServices/UserDataService/AdminUserDataService.cs b/DigitalLearningSolutions.Data/DataServices/UserDataService/AdminUserDataService.cs index 8af9cef6e5..91dcbd2efc 100644 --- a/DigitalLearningSolutions.Data/DataServices/UserDataService/AdminUserDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/UserDataService/AdminUserDataService.cs @@ -391,24 +391,30 @@ public IEnumerable GetAdminAccountsByUserId(int userId) search = search.Trim(); } - string BaseSelectQuery = $@"SELECT aa.ID, aa.UserID, aa.CentreID, aa.Active, aa.IsCentreAdmin, aa.IsReportsViewer, aa.IsSuperAdmin, aa.IsCentreManager, - aa.LastAccessed, aa.IsContentManager, aa.IsContentCreator, aa.IsSupervisor, aa.IsTrainer, aa.CategoryID, aa.IsFrameworkDeveloper, aa.IsFrameworkContributor,aa.ImportOnly, - aa.IsWorkforceManager, aa.IsWorkforceContributor, aa.IsLocalWorkforceManager, aa.IsNominatedSupervisor, - u.ID, u.PrimaryEmail, u.FirstName, u.LastName, u.Active, u.FailedLoginCount, - c.CentreID, c.CentreName, - ucd.ID, ucd.Email, ucd.EmailVerified, ucd.CentreID, - (SELECT count(*) - FROM ( - SELECT TOP 1 AdminSessions.AdminID FROM AdminSessions WHERE AdminSessions.AdminID = aa.ID - UNION ALL - SELECT TOP 1 FrameworkCollaborators.AdminID FROM FrameworkCollaborators WHERE FrameworkCollaborators.AdminID = aa.ID - UNION ALL - SELECT TOP 1 SupervisorDelegates.SupervisorAdminID FROM SupervisorDelegates WHERE SupervisorDelegates.SupervisorAdminID = aa.ID - ) AS tempTable) AS AdminIdReferenceCount + string BaseSelectQuery = $@"WITH AdminReferenceCounts AS ( + SELECT AdminID, SUM(AdminReferenceCount) AS AdminIdReferenceCount + FROM ( + SELECT DISTINCT AdminID, 1 AS AdminReferenceCount FROM AdminSessions + UNION ALL + SELECT DISTINCT AdminID, 1 AS AdminReferenceCount FROM FrameworkCollaborators + UNION ALL + SELECT DISTINCT SupervisorAdminID AS AdminID, 1 AS AdminReferenceCount FROM SupervisorDelegates + ) AS AdminReferenceRows + GROUP BY AdminID + ) + SELECT aa.ID, aa.UserID, aa.CentreID, aa.Active, aa.IsCentreAdmin, aa.IsReportsViewer, aa.IsSuperAdmin, aa.IsCentreManager, + aa.LastAccessed, aa.IsContentManager, aa.IsContentCreator, aa.IsSupervisor, aa.IsTrainer, aa.CategoryID, aa.IsFrameworkDeveloper, aa.IsFrameworkContributor,aa.ImportOnly, + aa.IsWorkforceManager, aa.IsWorkforceContributor, aa.IsLocalWorkforceManager, aa.IsNominatedSupervisor, + u.ID, u.PrimaryEmail, u.FirstName, u.LastName, u.Active, u.FailedLoginCount, + c.CentreID, c.CentreName, + ucd.ID, ucd.Email, ucd.EmailVerified, ucd.CentreID, + COALESCE(arc.AdminIdReferenceCount, 0) AS AdminIdReferenceCount, + COUNT(*) OVER() AS TotalCount FROM AdminAccounts AS aa INNER JOIN Users AS u ON aa.UserID = u.ID INNER JOIN Centres AS c ON aa.CentreID = c.CentreID LEFT OUTER JOIN - UserCentreDetails AS ucd ON u.ID = ucd.UserID AND c.CentreID = ucd.CentreID"; + UserCentreDetails AS ucd ON u.ID = ucd.UserID AND c.CentreID = ucd.CentreID + LEFT JOIN AdminReferenceCounts AS arc ON arc.AdminID = aa.ID"; string condition = $@" WHERE ((@adminId = 0) OR (aa.ID = @adminId)) AND (u.FirstName + ' ' + u.LastName + ' ' + u.PrimaryEmail + ' ' + COALESCE(ucd.Email, '') + ' ' + COALESCE(u.ProfessionalRegistrationNumber, '') LIKE N'%' + @search + N'%') AND @@ -426,30 +432,30 @@ FROM AdminAccounts AS aa INNER JOIN OFFSET @offset ROWS FETCH NEXT @rows ROWS ONLY"; - IEnumerable adminEntity = connection.Query( + var adminEntities = new List(); + int resultCount = 0; + + connection.Query( sql, - (adminAccount, userAccount, centre, userCentreDetails, adminIdReferenceCount) => new AdminEntity( - adminAccount, - userAccount, - centre, - userCentreDetails, - adminIdReferenceCount - ), + (adminAccount, userAccount, centre, userCentreDetails, adminIdReferenceCount, totalCount) => + { + resultCount = resultCount == 0 ? totalCount : resultCount; + var adminEntity = new AdminEntity( + adminAccount, + userAccount, + centre, + userCentreDetails, + adminIdReferenceCount + ); + adminEntities.Add(adminEntity); + return adminEntity; + }, new { adminId, search, centreId, userStatus, failedLoginThreshold, role, offset, rows }, - splitOn: "ID,ID,CentreID,ID,AdminIdReferenceCount", + splitOn: "ID,ID,CentreID,ID,AdminIdReferenceCount,TotalCount", commandTimeout: 3000 ); - int ResultCount = connection.ExecuteScalar( - @$"SELECT COUNT(*) AS Matches - FROM AdminAccounts AS aa INNER JOIN - Users AS u ON aa.UserID = u.ID INNER JOIN - Centres AS c ON aa.CentreID = c.CentreID LEFT OUTER JOIN - UserCentreDetails AS ucd ON u.ID = ucd.UserID AND c.CentreID = ucd.CentreID {condition}", - new { adminId, search, centreId, userStatus, failedLoginThreshold, role }, - commandTimeout: 3000 - ); - return (adminEntity, ResultCount); + return (adminEntities, resultCount); } public int RessultCount(int adminId, string search, int? centreId, string userStatus, int failedLoginThreshold, string role) { diff --git a/DigitalLearningSolutions.Data/DataServices/UserDataService/DelegateUserCardDataService.cs b/DigitalLearningSolutions.Data/DataServices/UserDataService/DelegateUserCardDataService.cs index f2068623a9..25cde5ae9d 100644 --- a/DigitalLearningSolutions.Data/DataServices/UserDataService/DelegateUserCardDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/UserDataService/DelegateUserCardDataService.cs @@ -40,7 +40,12 @@ public partial class UserDataService '' AS ProfessionalRegistrationNumber, NULL AS AdminID"; private const string DelegateUserCardSelectQuery = - @"SELECT + @"WITH ActiveAdminAccounts AS ( + SELECT UserID, CentreID, ID + FROM AdminAccounts + WHERE Active = 1 + ) + SELECT da.ID, da.CandidateNumber, c.CentreName, @@ -67,20 +72,21 @@ public partial class UserDataService da.Active, u.HasBeenPromptedForPrn, u.ProfessionalRegistrationNumber, - (SELECT ID - FROM AdminAccounts aa - WHERE aa.UserID = da.UserID - AND aa.CentreID = da.CentreID - AND aa.Active = 1 - ) AS AdminID + aaa.ID AS AdminID FROM DelegateAccounts AS da INNER JOIN Centres AS c ON c.CentreID = da.CentreID INNER JOIN Users AS u ON u.ID = da.UserID LEFT JOIN UserCentreDetails AS ucd ON ucd.UserID = da.UserID AND ucd.CentreID = da.CentreID - INNER JOIN JobGroups AS jg ON jg.JobGroupID = u.JobGroupID"; + INNER JOIN JobGroups AS jg ON jg.JobGroupID = u.JobGroupID + LEFT JOIN ActiveAdminAccounts AS aaa ON aaa.UserID = da.UserID AND aaa.CentreID = da.CentreID"; private const string DelegateUserSelectQuery = - @"SELECT + @"WITH ActiveAdminAccounts AS ( + SELECT UserID, CentreID, ID + FROM AdminAccounts + WHERE Active = 1 + ) + SELECT da.ID, da.Active AS DelegateActive, da.CandidateNumber, @@ -112,14 +118,14 @@ FROM DelegateAccounts AS da u.ProfessionalRegistrationNumber, u.PrimaryEmail, ucd.Email, - (SELECT ID - FROM AdminAccounts aa - WHERE aa.UserID = da.UserID - AND aa.CentreID = da.CentreID - AND aa.Active = 1 - ) AS AdminID "; + aaa.ID AS AdminID "; private const string DelegateUserExportSelectQuery = - @"SELECT + @"WITH ActiveAdminAccounts AS ( + SELECT UserID, CentreID, ID + FROM AdminAccounts + WHERE Active = 1 + ) + SELECT da.ID, da.CandidateNumber, c.CentreName, @@ -147,12 +153,7 @@ FROM AdminAccounts aa da.Active, u.HasBeenPromptedForPrn, u.ProfessionalRegistrationNumber, - (SELECT ID - FROM AdminAccounts aa - WHERE aa.UserID = da.UserID - AND aa.CentreID = da.CentreID - AND aa.Active = 1 - ) AS AdminID + aaa.ID AS AdminID ,u.PrimaryEmail ,ucd.Email ,da.Active as DelegateActive @@ -160,12 +161,14 @@ FROM DelegateAccounts AS da INNER JOIN Centres AS c ON c.CentreID = da.CentreID INNER JOIN Users AS u ON u.ID = da.UserID LEFT JOIN UserCentreDetails AS ucd ON ucd.UserID = da.UserID AND ucd.CentreID = da.CentreID - INNER JOIN JobGroups AS jg ON jg.JobGroupID = u.JobGroupID"; + INNER JOIN JobGroups AS jg ON jg.JobGroupID = u.JobGroupID + LEFT JOIN ActiveAdminAccounts AS aaa ON aaa.UserID = da.UserID AND aaa.CentreID = da.CentreID"; private const string DelegateUserFromTable = @" FROM DelegateAccounts AS da WITH (NOLOCK) INNER JOIN Centres AS c WITH (NOLOCK) ON c.CentreID = da.CentreID INNER JOIN Users AS u WITH (NOLOCK) ON u.ID = da.UserID LEFT JOIN UserCentreDetails AS ucd WITH (NOLOCK) ON ucd.UserID = da.UserID AND ucd.CentreID = da.CentreID - INNER JOIN JobGroups AS jg WITH (NOLOCK) ON jg.JobGroupID = u.JobGroupID "; + INNER JOIN JobGroups AS jg WITH (NOLOCK) ON jg.JobGroupID = u.JobGroupID + LEFT JOIN ActiveAdminAccounts AS aaa WITH (NOLOCK) ON aaa.UserID = da.UserID AND aaa.CentreID = da.CentreID "; private string DelegatewhereConditon = $@" Where ((CentreID = @centreId) OR (@centreId= 0)) AND ( FirstName + ' ' + LastName + ' ' + PrimaryEmail + ' ' + COALESCE(Email, '') + ' ' + COALESCE(CandidateNumber, '') LIKE N'%' + @searchString + N'%') AND ((@isActive = 'Any') OR (@isActive = 'true' AND DelegateActive = 1) OR (@isActive = 'false' AND DelegateActive = 0)) @@ -352,47 +355,26 @@ public List GetDelegateUserCardsForExportByCentreId(String sea if (sortBy == "SearchableName") orderBy = " ORDER BY LTRIM(LastName) " + sortDirection + ", LTRIM(FirstName) "; - else if(sortBy == "LastAccessed") + else if (sortBy == "LastAccessed") orderBy = " ORDER BY LastAccessed " + sortDirection; else orderBy = " ORDER BY DateRegistered " + sortDirection; orderBy += " OFFSET " + offSet + " ROWS FETCH NEXT " + itemsPerPage + " ROWS ONLY "; - var mainSql = "SELECT * FROM ( " + DelegateUserSelectQuery + DelegateUserFromTable + " ) D " + DelegatewhereConditon + orderBy; + var mainSql = "SELECT D.*, COUNT(*) OVER() AS TotalCount FROM ( " + DelegateUserSelectQuery + DelegateUserFromTable + " ) D " + DelegatewhereConditon + orderBy; - IEnumerable delegateUserCard = connection.Query( + var delegateUserCards = new List(); + int resultCount = 0; + + connection.Query( mainSql, - new + (delegateUserCard, totalCount) => { - searchString, - offSet, - itemsPerPage, - sortBy, - sortDirection, - centreId, - isActive, - isPasswordSet, - isAdmin, - isUnclaimed, - isEmailVerified, - registrationType, - jobGroupId, - groupId, - answer1, - answer2, - answer3, - answer4, - answer5, - answer6 + resultCount = resultCount == 0 ? totalCount : resultCount; + delegateUserCards.Add(delegateUserCard); + return delegateUserCard; }, - commandTimeout: 3000 - ); - - var delegateCountQuery = @$"SELECT COUNT(*) AS Matches FROM ( " + DelegateUserSelectQuery + DelegateUserFromTable + " ) D " + DelegatewhereConditon; - - int ResultCount = connection.ExecuteScalar( - delegateCountQuery, new { searchString, @@ -414,11 +396,13 @@ public List GetDelegateUserCardsForExportByCentreId(String sea answer3, answer4, answer5, - answer6, + answer6 }, + splitOn: "TotalCount", commandTimeout: 3000 ); - return (delegateUserCard, ResultCount); + + return (delegateUserCards, resultCount); } public List GetDelegatesNotRegisteredForGroupByGroupId(int groupId, int centreId) diff --git a/DigitalLearningSolutions.Data/DataServices/UserDataService/UserDataService.cs b/DigitalLearningSolutions.Data/DataServices/UserDataService/UserDataService.cs index 178e2f62dc..6c6af7ce4d 100644 --- a/DigitalLearningSolutions.Data/DataServices/UserDataService/UserDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/UserDataService/UserDataService.cs @@ -291,7 +291,7 @@ int centreId public bool PrimaryEmailInUseAtCentres(string email); public int? GetUserIdFromLearningHubAuthId(int learningHubAuthId); - void DeactivateAdminAccount(int userId, int centreId); + void DeactivateAdminAccount(int userId, int centreId); int? CheckDelegateIsActive(int delegateId); } @@ -606,50 +606,59 @@ public void UpdateUserDetailsAccount(string firstName, string lastName, string p ); } public (IEnumerable, int) GetAllDelegates( - string search, int offset, int rows, int? delegateId, string accountStatus, string lhlinkStatus, int? centreId, int failedLoginThreshold - ) + string search, int offset, int rows, int? delegateId, string accountStatus, string lhlinkStatus, int? centreId, int failedLoginThreshold) { if (!string.IsNullOrEmpty(search)) { search = search.Trim(); } - string BaseSelectQuery = @$"SELECT - da.ID, - da.Active, - da.CentreID, - ce.CentreName, - ce.Active AS CentreActive, - da.DateRegistered, - da.LastAccessed, - da.CandidateNumber, - da.Approved, - da.SelfReg, - da.UserID, - da.RegistrationConfirmationHash, - u.ID as UserId, - u.PrimaryEmail AS EmailAddress, - u.FirstName, - u.LastName, - u.Active as UserActive, - u.LearningHubAuthID, - u.EmailVerified, - ucd.ID as UserCentreDetailID, - ucd.UserID, - ucd.CentreID, - ucd.Email as CentreEmail, - ucd.EmailVerified as CentreEmailVerified, - (SELECT ID - FROM AdminAccounts aa - WHERE aa.UserID = da.UserID - AND aa.CentreID = da.CentreID - AND aa.Active = 1 - ) AS AdminID - FROM DelegateAccounts AS da WITH (NOLOCK) - INNER JOIN Centres AS ce WITH (NOLOCK) ON ce.CentreId = da.CentreID - INNER JOIN Users AS u WITH (NOLOCK) ON u.ID = da.UserID - LEFT JOIN UserCentreDetails AS ucd WITH (NOLOCK) ON ucd.UserID = u.ID - AND ucd.CentreId = da.CentreID - INNER JOIN JobGroups AS jg WITH (NOLOCK) ON jg.JobGroupID = u.JobGroupID"; + + string activeAdminAccountsCte = @" + WITH ActiveAdminAccounts AS ( + SELECT + aa.UserID, + aa.CentreID, + MIN(aa.ID) AS AdminID + FROM AdminAccounts AS aa WITH (NOLOCK) + WHERE aa.Active = 1 + GROUP BY aa.UserID, aa.CentreID + )"; + + string baseSelectQuery = @$"{activeAdminAccountsCte} + SELECT + da.ID, + da.Active, + da.CentreID, + ce.CentreName, + ce.Active AS CentreActive, + da.DateRegistered, + da.LastAccessed, + da.CandidateNumber, + da.Approved, + da.SelfReg, + da.UserID, + da.RegistrationConfirmationHash, + u.ID as UserId, + u.PrimaryEmail AS EmailAddress, + u.FirstName, + u.LastName, + u.Active as UserActive, + u.LearningHubAuthID, + u.EmailVerified, + ucd.ID as UserCentreDetailID, + ucd.UserID, + ucd.CentreID, + ucd.Email as CentreEmail, + ucd.EmailVerified as CentreEmailVerified, + aaa.AdminID + FROM DelegateAccounts AS da WITH (NOLOCK) + INNER JOIN Centres AS ce WITH (NOLOCK) ON ce.CentreId = da.CentreID + INNER JOIN Users AS u WITH (NOLOCK) ON u.ID = da.UserID + LEFT JOIN UserCentreDetails AS ucd WITH (NOLOCK) ON ucd.UserID = u.ID + AND ucd.CentreId = da.CentreID + LEFT JOIN ActiveAdminAccounts AS aaa WITH (NOLOCK) + ON aaa.UserID = da.UserID + AND aaa.CentreID = da.CentreID"; string condition = $@" WHERE ((@delegateId = 0) OR (da.ID = @delegateId)) AND (u.FirstName + ' ' + u.LastName + ' ' + u.PrimaryEmail + ' ' + COALESCE(ucd.Email, '') + ' ' + COALESCE(da.CandidateNumber, '') LIKE N'%' + @search + N'%') AND ((ce.CentreID = @centreId) OR (@centreId= 0)) AND ((@accountStatus = 'Any') OR (@accountStatus = 'Active' AND da.Active = 1 AND u.Active =1) OR (@accountStatus = 'Inactive' AND (u.Active = 0 OR da.Active =0)) @@ -657,7 +666,7 @@ LEFT JOIN UserCentreDetails AS ucd WITH (NOLOCK) ON ucd.UserID = u.ID OR (@accountStatus = 'Claimed' AND da.RegistrationConfirmationHash is null) OR (@accountStatus = 'Unclaimed' AND da.RegistrationConfirmationHash is not null)) AND ((@lhlinkStatus = 'Any') OR (@lhlinkStatus = 'Linked' AND u.LearningHubAuthID IS NOT NULL) OR (@lhlinkStatus = 'Not linked' AND u.LearningHubAuthID IS NULL))"; - string sql = @$"{BaseSelectQuery}{condition} ORDER BY LTRIM(u.LastName), LTRIM(u.FirstName) + string sql = @$"{baseSelectQuery}{condition} ORDER BY LTRIM(u.LastName), LTRIM(u.FirstName) OFFSET @offset ROWS FETCH NEXT @rows ROWS ONLY"; IEnumerable delegateEntity = connection.Query( @@ -667,13 +676,16 @@ OFFSET @offset ROWS ); int ResultCount = connection.ExecuteScalar( - @$"SELECT COUNT(*) AS Matches + @$"{activeAdminAccountsCte} + SELECT COUNT(*) AS Matches FROM DelegateAccounts AS da WITH (NOLOCK) INNER JOIN Centres AS ce WITH (NOLOCK) ON ce.CentreId = da.CentreID INNER JOIN Users AS u WITH (NOLOCK) ON u.ID = da.UserID LEFT JOIN UserCentreDetails AS ucd WITH (NOLOCK) ON ucd.UserID = u.ID AND ucd.CentreId = da.CentreID - INNER JOIN JobGroups AS jg WITH (NOLOCK) ON jg.JobGroupID = u.JobGroupID {condition}", + LEFT JOIN ActiveAdminAccounts AS aaa WITH (NOLOCK) + ON aaa.UserID = da.UserID + AND aaa.CentreID = da.CentreID {condition}", new { delegateId, search, centreId, accountStatus, failedLoginThreshold, lhlinkStatus }, commandTimeout: 3000 );