SQL 服务器查询速度慢 - 怎么优化呢?

SQL Server query slow - how do optimize it?

我需要一些帮助来优化此查询以使其更快。

此查询生成一个视图,后者将显示在网站的 table 中。 这个查询很慢,我正在努力让它更快。

到目前为止,我唯一尝试过的就是减少为 table.

检索的列数

这是查询:

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

这是一个非常片面的回答,但您问过如何在多个子查询中引用一次 table 而不是多次。

这是一个示例,说明如何将所有这些子查询替换为 Patients & Treatments table 以及城市 table。您真的需要了解联接。

FROM dbo.Clients AS C
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

然后您的列列表将子查询替换为 Patients 和 City,如下所示:

Cities.Name AS City,
Patients.TotalPatientCount,
Patients.ActivePatientCount,
Patients.LastPatientAddition,
Treatments.TotalTreatmentCount,
Treatments.LastTreatmentAddition,

这至少应该给你一个想法。

我将添加第二个答案,这是完整的 sql。这当然没有经过测试,因为我们无法访问您的数据,但我认为您应该能够自己调试它。这段代码中有很多指示应该告诉你如何去。

最基本的是取出所有相关查询并将它们作为子查询。这样做的唯一原因是您使用的所有 Max/Min - 根据您的数据,我会认为它们不是必需的,然后您应该将它们取出并直接加入表格。所有子查询都是左连接 - 如果可以,请根据您的数据再次将它们设为正常连接。

也把外面的组按Id去掉了,99%肯定没这个必要,Top 100%位也是。

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