Search code examples
sqlsql-serversql-server-2017

SQL Server query slow - how do optimize it?


I would like some help to optimize this query to be faster.

This query produces a view that latter will be showed in a table in a website. This query is slow, and I am trying to make it faster.

The only thing I tried till now is to reduce the amount of columns I retrieve for the table.

This is the query:

SELECT        TOP (100) PERCENT Id, MAX(BusinessTitle) AS BusinessTitle, MAX(ClientName) AS ClientName, MAX(ClientType) AS Type, MAX(CreatedWhen) AS CreatedWhen, MAX(CASE WHEN C.[CreatedBy] IS NULL 
                     THEN 'Client' ELSE 'Admin' END) AS CreatedBy, CAST(MAX(CASE WHEN C.IsDisabled = 1 THEN 1 ELSE 0 END) AS BIT) AS IsDisabled, MAX(ReferenceSource) AS ReferenceSource, MAX(OtherReferenceSource) 
                     AS OtherReferenceSource, MAX(Address) AS Address,
                         (SELECT        MAX(T.FirstName + ' ' + T.LastName) AS Expr1
                           FROM            dbo.ApplicationUsers AS A INNER JOIN
                                                     dbo.Therapists AS T ON A.UserName = MAX(C.ClientName) AND A.Id = T.ApplicationUserId) AS ClientAdmin,
                         (SELECT        MAX(A.Email) AS Expr1
                           FROM            dbo.ApplicationUsers AS A INNER JOIN
                                                     dbo.Therapists AS T ON A.UserName = MAX(C.ClientName) AND A.Id = T.ApplicationUserId) AS Email,
                         (SELECT        MAX(Name) AS Expr1
                           FROM            dbo.Cities AS CY
                           WHERE        (Id = MAX(C.CityId))) AS City,
                         (SELECT        COUNT(*) AS Expr1
                           FROM            dbo.Patients AS P
                           WHERE        (ClientId = C.Id)) AS TotalPatientCount,
                         (SELECT        COUNT(*) AS Expr1
                           FROM            dbo.Patients AS P
                           WHERE        (ClientId = C.Id) AND (IsDeleted = 0) AND (IsDisabled = 0)) AS ActivePatientCount,
                         (SELECT        MAX(CreatedWhen) AS Expr1
                           FROM            dbo.Patients AS P
                           WHERE        (ClientId = C.Id)) AS LastPatientAddition,
                         (SELECT        COUNT(*) AS Expr1
                           FROM            dbo.Treatments AS T
                           WHERE        (ClientId = C.Id)) AS TotalTreatmentCount,
                         (SELECT        MAX(CreatedWhen) AS Expr1
                           FROM            dbo.Treatments AS T
                           WHERE        (ClientId = C.Id)) AS LastTreatmentAddition,
                         (SELECT        COUNT(*) AS Expr1
                           FROM            dbo.Therapists AS T
                           WHERE        (ClientId = C.Id)) AS TotalTherapistCount,
                         (SELECT        COUNT(*) AS Expr1
                           FROM            dbo.Therapists AS T INNER JOIN
                                                     dbo.ApplicationUsers AS A ON T.ClientId = C.Id AND T.ApplicationUserId = A.Id
                           WHERE        (A.IsDeleted = 0) AND (A.IsDisabled = 0)) AS ActiveTherapistCount,
                         (SELECT        MAX(A.CreatedWhen) AS Expr1
                           FROM            dbo.Therapists AS T INNER JOIN
                                                     dbo.ApplicationUsers AS A ON T.ClientId = C.Id AND T.ApplicationUserId = A.Id) AS LastTherapistAddition,
                         (SELECT        MAX(A.LastLoginDate) AS Expr1
                           FROM            dbo.Therapists AS T INNER JOIN
                                                     dbo.ApplicationUsers AS A ON T.ClientId = C.Id AND T.ApplicationUserId = A.Id
                           WHERE        (A.LastLoginDate IS NOT NULL)) AS TherapistLastLoginDate, CAST((CASE WHEN
                         ((SELECT        COUNT(S.[Id])
                             FROM            [dbo].[ClientSubscriptions] AS S
                             WHERE        ((S.[ClientId] = C.[Id]) AND (S.[IsDeleted] = 0) AND ((S.[SubscriptionEnd] IS NULL) OR
                                                      (S.[SubscriptionEnd] > GETDATE())))) > 0) THEN 1 ELSE 0 END) AS BIT) AS HasActiveSubscription,
                         (SELECT        MAX(SubscriptionEnd) AS Expr1
                           FROM            dbo.ClientSubscriptions AS S
                           WHERE        (ClientId = C.Id) AND (IsDeleted = 0) AND (SubscriptionEnd IS NULL OR
                                                     SubscriptionEnd > GETDATE())) AS LastValidSubscriptionEnd, CAST((CASE WHEN
                         ((SELECT        COUNT(S.[Id])
                             FROM            [dbo].[ClientSubscriptions] AS S
                             WHERE        ((S.[ClientId] = C.[Id]) AND (S.[IsDeleted] = 0) AND ((S.[SubscriptionEnd] IS NULL) OR
                                                      (S.[SubscriptionEnd] > GETDATE())) AND (S.[Id] <>
                                                          (SELECT        MIN(S2.[Id])
                                                            FROM            [dbo].[ClientSubscriptions] AS S2
                                                            WHERE        ((S2.[ClientId] = C.[Id]) AND (S2.[IsDeleted] = 0)))))) > 0) THEN 1 ELSE 0 END) AS BIT) AS IsPayingCustomer, COALESCE
                         ((SELECT        MAX(MonthlyPrice) AS Expr1
                             FROM            dbo.ClientSubscriptions AS S
                             WHERE        (ClientId = C.Id) AND (IsDeleted = 0) AND (SubscriptionEnd IS NULL OR
                                                      SubscriptionEnd > GETDATE()) AND (MonthlyPrice > 0)), 0.00) AS ActiveSubscriptionMonthlyPrice, MAX(ClientStatus) AS Status, MAX(Phone1) AS Phone, MAX(Phone2) AS Phone2,
                         (SELECT        Code
                           FROM            dbo.DiscountCoupons AS DC
                           WHERE        (Code =
                                                         (SELECT        TOP (1) DiscountCouponCode
                                                           FROM            dbo.ClientPayments AS CP
                                                           WHERE        (ClientId = C.Id)
                                                           ORDER BY Id))) AS DiscountCouponCode,
                         (SELECT        IssuedTo
                           FROM            dbo.DiscountCoupons AS DC
                           WHERE        (Code =
                                                         (SELECT        TOP (1) DiscountCouponCode
                                                           FROM            dbo.ClientPayments AS CP
                                                           WHERE        (ClientId = C.Id)
                                                           ORDER BY Id))) AS DiscountCouponIssuedTo,
                         (SELECT        ClientDiscount
                           FROM            dbo.DiscountCoupons AS DC
                           WHERE        (Code =
                                                         (SELECT        TOP (1) DiscountCouponCode
                                                           FROM            dbo.ClientPayments AS CP
                                                           WHERE        (ClientId = C.Id)
                                                           ORDER BY Id))) AS DiscountCouponClientDiscount, COALESCE
                         ((SELECT        COUNT(Id) AS Expr1
                             FROM            dbo.ClientFiles AS F
                             WHERE        (ClientId = C.Id)), 0) AS TotalFilesCount, COALESCE
                         ((SELECT        SUM(FileSize) AS Expr1
                             FROM            dbo.ClientFiles AS F
                             WHERE        (ClientId = C.Id)), 0) / 1048576.0 AS TotalFilesSize, CAST(MAX(CASE WHEN C.CrmEnded = 1 THEN 1 ELSE 0 END) AS BIT) AS CrmEnded, MAX(CrmStatus) AS CrmStatus, MAX(CrmUnuseReason) 
                     AS CrmUnuseReason,
                         (SELECT        COUNT(1) AS Expr1
                           FROM            dbo.Tipulog_Crm_Calls_new AS CC
                           WHERE        (Cust_id = C.Id)) AS CrmCallCount
FROM            dbo.Clients AS C
WHERE        (IsDeleted = 0)
GROUP BY Id

Solution

  • I will add a second answer, which is the complete sql. This has of course not been tested as we have no access to your data, but I think you should be able to debug it yourself. There are many pointers in this code that should show you how to go.

    The basic thing is to take out all of the correlated queries and put them as subqueries. The only reason to do this is all the Max/Min you use - I would look at those as if they are not necessary depending on your data then you should take them out and join to the tables directly. All the subqueries are left joins - again make them normal joins if you can depending on your data.

    Also took out the outside group by Id, as 99% sure this is not necessary, as is the Top 100% bit.

    SELECT  BusinessTitle, ClientName, ClientType AS Type, CreatedWhen, 
            CASE WHEN C.[CreatedBy] IS NULL THEN 'Client' ELSE 'Admin' END) AS CreatedBy, 
            CAST(CASE WHEN C.IsDisabled = 1 THEN 1 ELSE 0 END AS BIT) AS IsDisabled, 
            ReferenceSource, OtherReferenceSource, Address,
            ApplicationByName.FullName AS ClientAdmin,
            ApplicationByName.Email AS Email,
            Cities.Name AS City,
            Patients.TotalPatientCount,
            Patients.ActivePatientCount,
            Patients.LastPatientAddition,
            Treatments.TotalTreatmentCount,
            Treatments.LastTreatmentAddition,
            Therapists.TotalTherapistCount,
            Therapists.ActiveTherapistCount,
            Therapists.LastTherapistAddition,
            Therapists.TherapistLastLoginDate
            CAST(CASE WHEN Subscriptions.SubscriptionCount>0 then 1 else 0 end as BIT) as HasActiveSubscription,
            Subscriptions.LastValidSubscriptionEnd
            CAST(Subscriptions.IsPayingCustomer AS BIT) AS IsPayingCustomer, 
            COALESCE(ActiveSubscriptionMonthlyPrice,0) as ActiveSubscriptionMonthlyPrice
            ClientStatus AS Status, Phone1 AS Phone, Phone2 AS Phone2,
            ClientPayments.DiscountCouponCode,
            DiscountCoupons.IssuedTo AS DiscountCouponIssuedTo,
            DiscountCoupons.ClientDiscount AS DiscountCouponClientDiscount, 
            COALESCE(ClientFiles.TotalFilesCount,0) AS TotalFilesCount, 
            COALESCE(ClientFiles.TotalFilesSize,0) AS TotalFilesSize, 
            CAST((CASE WHEN C.CrmEnded = 1 THEN 1 ELSE 0 END) AS BIT) AS CrmEnded, 
            CrmStatus, CrmUnuseReason,
            Crm_Calls.CrmCallCount
    FROM dbo.Clients AS C
    left join (
        select A.UserName,
            max(T.FirstName + ' ' + T.LastName) as FullName,
            max(A.Email) as Email
        from dbo.ApplicationUsers A
        join dbo.Therapists T on T.ApplicationUserId=A.Id
        group by A.Username
        ) ApplicationByName on ApplicationByName.UserName=C.ClientName
    join dbo.Cities on Cities.ID=c.CityID
    left join (
        SELECT ClientId,
            COUNT(*) AS TotalPatientCount,
            sum(case when IsDeleted = 0 AND IsDisabled = 0 then 1 else 0 end) AS ActivePatientCount,
            MAX(CreatedWhen) AS LastPatientAddition
        FROM  dbo.Patients
        GROUP BY ClientId
        ) Patients on Patients.ClientId = C.Id
    left join (
        SELECT ClientId,
            COUNT(*) AS TotalTreatmentCount,
            MAX(CreatedWhen) AS LastTreatmentAddition
        FROM dbo.Treatments
        GROUP BY ClientId
        ) Treatments on Treatments.ClientID = C.Id
    left join (
        select T.ClientId, 
            count(distinct T.Id) as TotalTherapistCount,
            sum(case when A.IsDeleted = 0 AND A.IsDisabled = 0 then 1 else 0 end) as ActiveTherapistCount,
            max(A.CreatedWhen) as LastTherapistAddition,
            max(A.LastLoginDate) as TherapistLastLoginDate
        from Therapists T
        left join dbo.ApplicationUsers A on A.Id=T.ApplicationUserId 
        group by T.ClientId
        ) Therapists on Therapists.ClientID = C.Id
    left join (
        SELECT S.ClientId,
            count(*) as SubscriptionCount,
            MAX(SubscriptionEnd) as LastValidSubscriptionEnd,
            MAX(case when MinSub.Id!=S.ID then 1 else 0 end as IsPayingCustomer,
            max(case when MonthlyPrice>0 then 0 end) as ActiveSubscriptionMonthlyPrice
        FROM dbo.ClientSubscriptions S
        join (
            select ClientId, min(Id) as Id 
            from dbo.ClientSubscriptions 
            where IsDeleted=0 
            group by ClientId
            ) MinSub on MinSub.ClientId=ClientSubscriptions.ClientId
        where IsDeleted=0 and (SubscriptionEnd is null or SubscriptionEnd>getdate())
        group by ClientId
        ) Subscriptions on Subscriptions.ClientId=C.Id
    left join (
        select ClientId, 
            DiscountCouponCode,
            row_number() over(partition by ClientId, order by Id) rn
        from  dbo.ClientPayments
        ) ClientPayments on ClientPayments.ClientId=C.ID and rn=1
    left join dbo.DiscountCoupons on DiscountCoupons.Code=ClientPayments.DiscountCouponCode
    left join (
        select ClientId,
            count(*) as TotalFilesCount,
            sum(FileSize)/1048576.0 as TotalFilesSize
        from dbo.ClientFiles
        group by ClientId
        ) ClientFiles on ClientFiles.ClientId=Client.Id
    left join (
        SELECT Cust_id, COUNT(1) AS CrmCallCount
        FROM dbo.Tipulog_Crm_Calls_new 
        group by Cust_id
        ) Crm_Calls on Crm_Calls.Cust_id=C.Id
    WHERE C.IsDeleted = 0