我正在做一些修改以提高 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)