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

时间:2015-06-19 22:00:31

标签: sql sql-server tsql query-optimization

我正在尝试优化以下查询,根据执行计划,内部查询中的排序成本很高。可以重写以下查询,以便它易于阅读并且表现良好吗?

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)

3 个答案:

答案 0 :(得分:1)

;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

答案 1 :(得分:0)

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

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
    )

但是,如果未从payvacation表中选择任何列,则会忽略整个LEFT JOIN。如果确实从payvacation表中选择了一列,那么在执行计划中确实会得到一个代价高昂的Sort运算符。为了消除它,我会创建一个索引视图,如下所示:

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。

答案 2 :(得分:0)

在我们正确回答这个问题之前,您需要解决几件事情。

  1. 确保查询按原样运行。由于payvoucher.claimid,您提供给我们的版本无法编译。我们可以猜到它应该是什么,但当事实证明是不同的时候,没有任何用处。
  2. 你可能在Case Insensitive环境中运行它,它可能会在那里运行,但通常你应该尝试保留你的表,字段,变量名称和案例结果'。 (作为.NET从业者,无论如何这应该是第二天性=)
  3. 有关表格定义,索引和对所涉及记录数量的估计以及数据交互方式的帮助将会有所帮助。(很多这种情况只会连接到其他几个......)
  4. 如果您能告诉我们您的期望以及这些表格中的其他流程以及我们的解决方案对这些流程的影响有多严重,那么您将获得额外奖励。 (我们可能会使SELECT超快,但代价是使INSERT / UPDATE / DELETE相当慢)
  5. (最后,摆脱NOLOCK提示,或将其更改为同义词READUNCOMMITTED并考虑一下,如果您仍然喜欢它们,就像现在一样;