重写查询以删除 sql 中的内部查询以进行优化

rewrite query to remove inner query in tsql to optimize

我正在尝试优化以下查询,根据执行计划,内部查询中的排序成本很高。是否可以重写以下查询以使其易于阅读和执行良好?

select 
     CL.col1, CL.col2 
FROM 
     CLAIM CL WITH (NOLOCK) 
     INNER JOIN MEMBER MEM WITH (NOLOCK) ON MEM.MEMID=CL.MEMID 
     LEFT JOIN PAYVACATION PV WITH (NOLOCK) ON CL.CLAIMID = PV.CLAIMID 
         and pv.paymentid =
              (select top 1 PAYVACATION.paymentid 
                 from PAYVACATION WITH (NOLOCK), 
                      payment WITH (NOLOCK) 
               where 
                    payvoucher.claimid = cl.claimid 
                    and PAYVACATION.paymentid = payment.paymentid 
                      order by payment.paystatusdate desc)
;WITH CTE AS
(
 select CL.col1, CL.col2, cl.claimid
  FROM CLAIM CL WITH (NOLOCK) 
  INNER JOIN MEMBER MEM WITH (NOLOCK)     ON MEM.MEMID=CL.MEMID 
  LEFT  JOIN PAYVACATION PV WITH (NOLOCK) ON CL.CLAIMID = PV.CLAIMID 
 ),
CTE2 AS 
(
select PAYVACATION.paymentid , PAYVACATION.claimid
      ,ROW_NUMBER() OVER (PARTITION BY PAYVACATION.claimid 
                     ORDER BY payment.paystatusdate desc) rn 
 from PAYVACATION WITH (NOLOCK)
 INNER JOIN payment WITH (NOLOCK) ON PAYVACATION.paymentid = payment.paymentid 
 INNER JOIN CTE WITH (NOLOCK)     ON PAYVACATION.claimid = cl.claimid 
)
SELECT CL.col1, CL.col2
FROM CTE CL 
INNER JOIN CTE2 C2 ON C2.claimid = CL.claimid
                  AND C2.rn = 1

假设 payvoucher.claimid 实际上是指 payvacation table,您可以像这样格式化您的查询:

SELECT c.col1, c.col2
FROM claim c
INNER JOIN member m ON m.memid=c.memid
LEFT JOIN payvacation pv1 ON c.claimid = pv1.claimid
    AND pv1.paymentid = (
        SELECT TOP 1 pv2.paymentid
        FROM payvacation pv2
        INNER JOIN payment p ON pv2.paymentid = p.paymentid
        WHERE pv2.claimid = cl.claimid
        ORDER BY payment.paystatusdate DESC
    )

但是,如果您不select payvacation table 中的任何列,整个 LEFT JOIN 将被忽略。如果您从 payvacation table 中执行 select 列,您确实会在执行计划中得到一个代价高昂的排序运算符。为了消除它,我会创建一个索引视图,如下所示:

CREATE VIEW indexed_view
WITH SCHEMABINDING AS
SELECT pv.paymentid, pv.claimid, p.paystatusdate
FROM dbo.payvacation pv
INNER JOIN dbo.payment p ON pv.paymentid = p.paymentid

GO
CREATE UNIQUE CLUSTERED INDEX PK_indexed_view ON indexed_view (paymentid)
CREATE INDEX i2 ON indexed_view (claimid, paystatusdate) INCLUDE (paymentid)

然后在子查询中使用索引视图,使用 NOEXPAND 提示:

SELECT c.col1, c.col2, pv1.paymentid
FROM claim c
INNER JOIN member m ON m.memid=c.memid
LEFT JOIN payvacation pv1 ON c.claimid = pv1.claimid
    AND pv1.paymentid = (
        SELECT TOP 1 iv.paymentid
        FROM dbo.indexed_view iv WITH (NOEXPAND)
        WHERE iv.claimid = c.claimid
        ORDER BY iv.paystatusdate DESC
    )

使用一些随机样本数据,第一次查询的查询成本为 190.9,第二次查询的成本为 4.96。

在我们正确回答这个问题之前,您需要解决一些问题。

  1. 确保查询按原样工作。由于 payvoucher.claimid,您提供给我们的版本将无法编译。我们可以猜到它应该是什么,但是当它被证明是不同的东西时,再努力也没有用。
  2. 你可能 运行 这在不区分大小写的环境中,它可能会在那里工作,但通常你应该尽量保持你的 table,字段,变量名称 'case-consequent' . (作为 .NET 从业者,这应该是第二天性 =)
  3. table-定义、索引和对所涉及记录数量的估计以及数据交互方式(如果可能)的估计会有所帮助。 ..)
  4. 如果您能告诉我们您的 期望 以及这些 table 上的其他流程以及我们的解决方案对这些的影响有多严重,将会有额外的好处。 (我们可能可以使 SELECT 超快,但代价是使 INSERT/UPDATE/DELETE 相当慢)

(最后,摆脱 NOLOCK 提示,或将它们更改为同义词 READUNCOMMITTED 并考虑一下您是否仍然像现在一样喜欢它们)