我在 SQL Server 中有一个复杂的查询,需要 70 秒才能完成 3,000,000 条记录。
当我删除两个
ORDER BY
子句时,只需要 0.5 秒即可完成。
SELECT TOP 100
ROW_NUMBER() OVER ( ORDER BY format([DetectionDateTime],'yyyy/MM/dd') DESC , [DetectionTime] DESC) AS RowId,
*
FROM (
SELECT
dbo.tblValidDetections.Id,
dbo.tblCameras.CameraName,
TRY_CAST((TRY_CAST(dbo.tblValidDetections.DetectionDate as varchar)
+ ' '
+ TRY_CAST(dbo.tblValidDetections.DetectionTime As varchar)) as datetime)
as DetectionDateTime,
format(dbo.tblValidDetections.DetectionDate, 'yyyy/MM/dd', 'fa') AS DetectionDatePersian,
dbo.tblValidDetections.DetectionTime,
TRY_CAST(dbo.tblValidDetections.DetectionTime as varchar) AS DetectionTimeAsString,
dbo.tblValidDetections.PelakCitySection,
dbo.tblValidDetections.PelakRightSection,
dbo.tblValidDetections.PelakAlphabetSection,
dbo.tblValidDetections.PelakLeftSection,
dbo.tblValidDetections.PelakCitySection + N' - ' +
dbo.tblValidDetections.PelakRightSection + N' ' +
dbo.tblValidDetections.PelakAlphabetSection + N' ' +
dbo.tblValidDetections.PelakLeftSection
AS Pelak,
CASE
WHEN dbo.tblValidDetections.Direction = 1 THEN N'In'
WHEN dbo.tblValidDetections.Direction = 0 THEN N'Out'
ELSE N'N/A'
END AS CarDirection,
dbo.__tbl_base_PelakTypesFarsi.pelakType,
dbo.__tbl_base_ProvinceList.ProvinceName,
dbo.__tbl_base_CityList.CityName
FROM dbo.tblValidDetections
INNER JOIN dbo.tblCameras
ON dbo.tblValidDetections.DetectionCameraId = dbo.tblCameras.Id
INNER JOIN dbo.__tbl_base_PelakTypesFarsi
ON dbo.tblValidDetections.PelakTypeId = dbo.__tbl_base_PelakTypesFarsi.Id
LEFT OUTER JOIN dbo.__tbl_base_CityList
ON dbo.tblValidDetections.CityId = dbo.__tbl_base_CityList.Id
LEFT OUTER JOIN dbo.__tbl_base_ProvinceList
ON dbo.tblValidDetections.ProvinceId = dbo.__tbl_base_ProvinceList.Id
AND dbo.__tbl_base_CityList.ProvinceId = dbo.__tbl_base_ProvinceList.Id
) As DetectionView
ORDER BY
format([DetectionDateTime],'yyyy/MM/dd') DESC,
[DetectionTime] DESC
我已将
DetectionDate
转换为 fa
文化以获取波斯日历日期,我认为这与此处的性能无关。
正如其他人所说,主要问题是您在
ORDER BY
子句中使用了计算值 - 无论是对于主查询还是在 ROW_NUMBER()
计算中。这消除了 SQL Server 可以使用索引有效地将查询结果限制为最近 100 行或计算 ROW_NUMBER()
仅针对这些行。
因此,首先要做的就是在两个地方用直接列引用替换这些
ORDER BY
表达式 - ORDER BY DetectionDate DESC, DetectionTime DESC
。您还需要确保 tblValidDetections(DetectionDate, DetectionTime)
上有适当的索引,或者以这两列开头的索引。
据我所知,不再需要子选择,因此可以将其消除。
最后,为了减少混乱并提高可读性,定义表别名并使用它们来限定所有列引用是一种很好的做法。
结果会是这样的:
SELECT TOP 100
ROW_NUMBER() OVER (ORDER BY VD.DetectionDate DESC, VD.DetectionTime DESC) AS RowId,
VD.Id,
C.CameraName,
TRY_CAST((TRY_CAST(VD.DetectionDate as varchar)
+ ' '
+ TRY_CAST(VD.DetectionTime As varchar)) as datetime)
as DetectionDateTime,
format(VD.DetectionDate, 'yyyy/MM/dd', 'fa') AS DetectionDatePersian,
VD.DetectionTime,
TRY_CAST(VD.DetectionTime as varchar) AS DetectionTimeAsString,
VD.PelakCitySection,
VD.PelakRightSection,
VD.PelakAlphabetSection,
VD.PelakLeftSection,
VD.PelakCitySection + N' - ' +
VD.PelakRightSection + N' ' +
VD.PelakAlphabetSection + N' ' +
VD.PelakLeftSection
AS Pelak,
CASE
WHEN VD.Direction = 1 THEN N'In'
WHEN VD.Direction = 0 THEN N'Out'
ELSE N'N/A'
END AS CarDirection,
PTF.pelakType,
PL.ProvinceName,
CL.CityName
FROM dbo.tblValidDetections VD
INNER JOIN dbo.tblCameras C
ON VD.DetectionCameraId = C.Id
INNER JOIN dbo.__tbl_base_PelakTypesFarsi PTF
ON VD.PelakTypeId = PTF.Id
LEFT OUTER JOIN dbo.__tbl_base_CityList CL
ON VD.CityId = CL.Id
LEFT OUTER JOIN dbo.__tbl_base_ProvinceList PL
ON VD.ProvinceId = PL.Id
AND CL.ProvinceId = PL.Id
ORDER BY VD.DetectionDate DESC, VD.DetectionTime DESC