首页 > 解决方案 > SQL子查询的替代方式是什么?

问题描述

我需要在第一个约会日期显示所有患者的信息。在这里,我附上了我所需信息的图像。请看一看:

在此处输入图像描述

我解决了它,但我想更有效地做到这一点。这是我的解决方案:

SELECT ROW_NUMBER() OVER(ORDER BY p.Name) SINo,
       or1.PatientID                RegNo,
       p.Name                       PatientName,
       or1.DataHead                 Diagnosis,
       or1.AppointmentDate,
       'First Appointment Date' = (
           SELECT or2.AppointmentDate
           FROM   OPDConsultancyRepository AS or2
           WHERE  or2.OPDConsultancyRepositoryID = (
                      (
                          SELECT MIN(or3.OPDConsultancyRepositoryID)
                          FROM   OPDConsultancyRepository AS or3
                          WHERE  or3.DataType = 3
                                 AND or3.DoctorID = 2139 AND or3.PatientID=or1.PatientID
                          GROUP BY
                                 or3.PatientID
                      )
                  )
       )
FROM   OPDConsultancyRepository  AS or1
       INNER JOIN Patient        AS p
            ON  p.PatientID = or1.PatientID
WHERE  or1.DataType = 3
       AND or1.DoctorID = 2139
ORDER BY
       p.Name

执行计划:

在此处输入图像描述

Above query needs 6 sec to produce output.那么有什么建议我如何优化它?

标签: sqlsql-servertsqlsql-server-2014

解决方案


如果没有 DDL 和执行计划,就不可能确切地说出是什么让你慢了下来。肯定会减慢您速度的一件事是相关子查询。请注意这篇文章:隐藏的 RBAR:Triangular Joins Second - 失去那个ORDER BY p.name你不需要的决赛。如果必须为显示目的对输出进行排序,则让应用程序处理它。最后,您应该检查最昂贵的执行计划并在需要的地方添加索引。

我将构建此查询的方式是首先将其设置为您拥有所有列的点,包括所有相关的患者ID 和约会日期记录。

DECLARE @table TABLE (patientId INT, appointmentDate DATE);
INSERT  @table VALUES (1,GETDATE()),(1,GETDATE()-3),(2,GETDATE()-10),(2,GETDATE()-20),
                      (3,GETDATE()-30),(4,GETDATE()-3),(4,GETDATE()),(4,GETDATE()-100);

SELECT t.patientId, t.appointmentDate FROM @table AS t;

您可以将逻辑转换为索引视图,其中 UNIQUE CLUSTERED INDEX 位于 PatientID 和 AppointmentDate 上。然后将该查询转换为子查询,您有几个选择:

DECLARE @table TABLE (patientId INT, appointmentDate DATE);
INSERT  @table VALUES (1,GETDATE()),(1,GETDATE()-3),(2,GETDATE()-10),(2,GETDATE()-20),
                      (3,GETDATE()-30),(4,GETDATE()-3),(4,GETDATE()),(4,GETDATE()-100);

-- Option #1  -- GROUP BY + MIN
SELECT   t.patientId, MIN(t.appointmentDate)
FROM     @table AS t
GROUP BY t.patientId;

-- Option #2  -- Partitioned ROW_NUMBER() filtered for WHERE rn=1 in subquery
SELECT 
  d.patientId,
  d.appointmentDate
FROM 
(
  SELECT 
    t.patientId, 
    t.appointmentDate, 
    rn = ROW_NUMBER() OVER (PARTITION BY t.patientId ORDER BY appointmentDate)
  FROM @table AS t
) AS d
WHERE d.rn = 1;

-- Option #3  -- TOP (1) WITH TIES + ROW_NUMBER()
SELECT   TOP (1) WITH TIES t.*
FROM     @table AS t
ORDER BY ROW_NUMBER() OVER (PARTITION BY t.patientId ORDER BY appointmentDate);

推荐阅读