0

我正在做一些修改以提高 1000 年前编写的旧报表查询的性能。在修改它以执行 UNION ALL 的过程中,我已将其中一个列选择值替换为 NULL。一旦我这样做,查询就会从执行 1-2 秒变为执行 30 秒。我查看了这两个版本的实际执行计划,它们看起来相同。对我来说,选择文字 NULL 可能比读取行值慢,这对我来说毫无意义。我还尝试过将 NULL 显式转换为前一种类型(nvarchar)以及选择 '' 而不是 NULL 没有区别。

查询和模式非常复杂,因此这可能需要一些问答疑难解答。选择 NULL 时减慢速度的列是底部附近的“OtherComments”。您可以看到运行速度很快的原始版本,并在其上方进行了评论。只是为了让我们保持正轨,我只是想了解为什么更改该列会使其运行缓慢,而不是其他改进查询的方法(我知道有很多)。这是一个缩写版本:

SELECT @Date                                         Parameter,
   fml.FirstName + ' ' + fml.LastName                ParentName,
   (
           SELECT TOP 1 p.PhoneNum
           FROM   tbl_Phone p, tbl_PhoneTypes pt, tbl_FamilyPhone fp
           WHERE  p.fk_PhoneTypeID = pt.pk_PhoneTypeID
           AND    p.pk_PhoneID = fp.fk_PhoneID
           AND    fp.fk_FamilyID = fml.pk_familyID
           AND    pt.Type = 'home' 
           AND    fp.IsDeleted = 0
           ORDER BY fp.CreatedDate DESC
   )                                                 PhoneNo,
   fml.Comments                                      FamilyComments,
   std.FirstName + ' ' + std.LastName                StudentName,
   (
           SELECT
           ...
   )                                                 ClassDescription,

   (
           SELECT
           ...
   )                                                 TestClassDescription,
   CASE
      WHEN (sce.pk_StudentEnrollmentID IS NOT NULL) THEN
         (
         SELECT emp.FirstName + ' ' + emp.LastName
         ...
         )
      WHEN (st.pk_StudentTestID IS NOT NULL) THEN
         (
         SELECT emp.FirstName + ' ' + emp.LastName
         ...
         )
      ELSE
         NULL
   END                                               InstructorName,
        CASE
              WHEN (st.pk_StudentTestID IS NOT NULL) THEN
                 (
                 SELECT emp.FirstName + ' ' + emp.LastName
                 ...
                 )
              ELSE
                 NULL
               END                                       TestInstructorName,
   st.TestDate                                      TestDate,
   tr.Description                                    TestResult,
       CASE
          WHEN (
           SELECT COUNT(ClassDate)                                                  --Select absent attendances from yesterday
           ...
           ) >= 1 
          THEN
               CAST(1 AS BIT)
          ELSE 
               CAST(0 AS BIT)
       END
                                                         MissedYesterdaysClass,
   CASE
      WHEN (datediff(day, CONVERT(varchar(11),fml.InquiryDate,102), @Date) =3 and
           (fml.CurrentMembershipDate IS NULL) AND ((fml.WebCreated = 0) OR (fml.WebCreated = NULL)) AND
           ((
           SELECT count(*)
           ...
           ) <= 0) AND
           ((
           SELECT count(*)
           ...
           ) <= 0) AND
           ((
           SELECT count(*)
           ...
           ) <= 0) AND
           ((
           SELECT count(*)
           ...
           ) <= 0) AND
           ((
           SELECT count(*)
           ...
           ) <= 0)) THEN
         CAST(1 AS BIT)
      WHEN (((fml.InquiryDate + 3) = @Date) AND (std.pk_StudentID IS NULL)) THEN
         CAST(1 AS BIT)
      ELSE
         CAST(0 AS BIT)
   END                                               InquiredButDidnotSchedule,
   CASE
      WHEN --(((st.TestDate + 2) = @Date) AND
            (datediff(day, CONVERT(varchar(11),st.testdate,102), @Date) =2 and
           (tr.Description = 'Not Enrolled') AND
               (st.IsDeleted = 0) AND
           (st.IsCancelled = 0)) THEN
         CAST(1 AS BIT)
      ELSE
         CAST(0 AS BIT)
   END                                               AttendedButHavenotEnrolled,
   CASE
      WHEN --(((st.TestDate + 1) = @Date) AND
                (datediff(day, CONVERT(varchar(11),st.testdate,102), @Date) =1 and
           (tr.Description = 'No Show')  AND
               (st.IsDeleted = 0) AND
           (st.IsCancelled = 0)) THEN
         CAST(1 AS BIT)
      ELSE
         CAST(0 AS BIT)
   END                                               PeopleNoShowed,
   CASE
      WHEN ---(((st.TestDate - 1) = @Date) AND
            (datediff(day, CONVERT(varchar(11),st.testdate,102), @Date) =-1 and
           (tr.Description = 'Scheduled')  AND
               (st.IsDeleted = 0) AND
           (st.IsCancelled = 0)) THEN
         CAST(1 AS BIT)
      ELSE
         CAST(0 AS BIT)
   END                                               PeopleHaveTest,
       CAST(0 AS BIT)                                    ShowOtherComments,

CASE
          WHEN  
                ((   
                SELECT count(*)
                ...
                ) > 0) AND
                ((
                SELECT EventDate
                ...
                )
                    BETWEEN 
                    (
                        DateAdd(day, DateDiff(day, 0, @Date), 0)
                    )
                    AND
                    (
                        (DateAdd(day, DateDiff(day, 0, @Date), 0) + 6)
                    )
                )
                AND
                datename(weekday, @Date) = 'Wednesday'
                AND
                bb.IsDeleted = 0
                AND
                bb.IsCancelled = 0 THEN
             CAST(1 AS BIT)
          ELSE
             CAST(0 AS BIT)
       END                                               ShowUpcomingBookingss,
       (
       SELECT EventDate
       ...
       )                                                 BookingDate,
       bb.ChildTurningAge                                Age,
   std.pk_StudentID                                  StudentID,
       fml.pk_familyID                                   FamilyID,
       CASE
      WHEN (datediff(day, CONVERT(varchar(11),fml.InquiryDate,102), @Date) =1 and
           (fml.MembershipDate IS NULL) AND (fml.WebCreated = 1) AND
           ((
           SELECT count(*)
           ...
           ) <= 0) AND
           ((
           SELECT count(*)
           ...
           ) <= 0) AND
           ((
           SELECT count(*)
           ...
           ) <= 0) AND
           ((
           SELECT count(*)
           ...
           ) <= 0) AND
           ((
           SELECT count(*)
           ...
           ) <= 0)) THEN
         CAST(1 AS BIT)
      WHEN (((fml.InquiryDate + 1) = @Date) AND (std.pk_StudentID IS NULL)) THEN
         CAST(1 AS BIT)
      ELSE
         CAST(0 AS BIT)
   END                                               InquiredButDidnotScheduleOnline,

--     Commenting this out and replacing with NULL slows it down from 2 sec to 30 sec
--     fml.OtherComment                        OtherComments,
       NULL                       OtherComments,
    FROM   tbl_Family fml
    LEFT OUTER JOIN tbl_Student std on fml.pk_FamilyID = std.fk_FamilyID
    LEFT OUTER JOIN tbl_StudentEnrollment sce on std.pk_StudentID = sce.fk_StudentID
    LEFT OUTER JOIN tbl_StudentTest st on std.pk_StudentID = st.fk_StudentID
    LEFT OUTER JOIN tbl_Booking bb on std.pk_StudentID = bb.fk_StudentID
    LEFT JOIN tbl_TestResult tr on st.fk_TestResultID = tr.pk_TestResultID
    WHERE  fml.fk_FacilityID = @FacilityID
    AND    (fml.IsDeleted = 0 OR fml.IsDeleted IS NULL)
    AND    (std.IsDeleted = 0 OR std.IsDeleted IS NULL)
4

1 回答 1

1

我比较了查询计划的实际 XML,并注意到快速存储过程的内存授予高于慢存储过程。这在 MS 论坛中向我解释过,可能是因为当 fml.OtherComment 在查询中时,它期望更大的行大小。如果您在编写良好的查询时遇到此问题,我不知道解决方案,但在我的情况下,这个 spoc 效率非常低(以一轮左右的方式选择最终记录),我从头开始重写它,它是现在跑得很快。

于 2012-12-14T18:34:09.083 回答