SQL子查询的替代方法是什么?

时间:2018-10-08 10:47:39

标签: sql sql-server tsql sql-server-2014

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

enter image description here

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

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

执行计划:

enter image description here

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

4 个答案:

答案 0 :(得分:0)

我认为您想要一个窗口函数:

min(or1.AppointmentDate) over (partition by doctorid, datetype, patientid) as first_appointment_date

答案 1 :(得分:0)

尝试这样的事情:

SELECT 
  ...
  [First Appointment Date] = 
    min(iif(or1.DataType = 3 and or1.DoctorID = 2139, or1.AppointmentDate, null))
      over(partition by or1.PatientID) 
  ...

尽管我想知道DoctorID,但尝试维护您的逻辑

删除这部分看起来很诱人

  

和or1.DoctorID = 2139

答案 2 :(得分:0)

这可能对您有用-您需要在内部查询中使用Lead()和Lag()函数。

https://blog.sqlauthority.com/2011/11/15/sql-server-introduction-to-lead-and-lag-analytic-functions-introduced-in-sql-server-2012/

选项-2

如果您使用自我联接并使用CTE,则可以通过对性能有重大意义的查询来实现相同的目的。

答案 3 :(得分:0)

如果没有DDL和执行计划,就无法确切地说出是什么在拖慢您的速度。确实使您放慢速度的一件事是相关子查询。请注意本文:Hidden RBAR: Triangular Joins其次-输掉最后一个ORDER BY p.name,您不需要。如果必须出于显示目的对输出进行排序,则让应用程序处理它。最后,您应该检查执行计划中最昂贵的内容,并在需要的地方添加索引。

我构建此查询的方式是首先使其到达一个位置,在此位置您所有的列都包括所有相关的PatientID和约会日期记录。

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和约会日期上。然后将该查询转换为子查询,您有几种选择:

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);