选择文字而不是列值时,查询速度会降低

时间:2012-11-27 14:55:15

标签: tsql sql-server-2005 optimization

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

查询和架构非常复杂,因此可能需要进行一些Q& A故障排除。选择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)

1 个答案:

答案 0 :(得分:1)

我比较了查询计划的实际XML,并注意到快速sproc的内存授权高于慢sproc。这在MS论坛中向我解释,可能是因为当fml.OtherComment在查询中时,它期望更大的行大小。我不知道一个解决方案,如果你用一个写得很好的查询遇到这个,但在我的情况下,这个spoc是如此低效(选择最后的记录在一个方向),我从头开始重写它,它的现在跑得快。