重写查询以删除 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。
在我们正确回答这个问题之前,您需要解决一些问题。
- 确保查询按原样工作。由于
payvoucher.claimid
,您提供给我们的版本将无法编译。我们可以猜到它应该是什么,但是当它被证明是不同的东西时,再努力也没有用。
- 你可能 运行 这在不区分大小写的环境中,它可能会在那里工作,但通常你应该尽量保持你的 table,字段,变量名称 'case-consequent' . (作为 .NET 从业者,这应该是第二天性 =)
- table-定义、索引和对所涉及记录数量的估计以及数据交互方式(如果可能)的估计会有所帮助。 ..)
- 如果您能告诉我们您的 期望 以及这些 table 上的其他流程以及我们的解决方案对这些的影响有多严重,将会有额外的好处。 (我们可能可以使 SELECT 超快,但代价是使 INSERT/UPDATE/DELETE 相当慢)
(最后,摆脱 NOLOCK
提示,或将它们更改为同义词 READUNCOMMITTED
并考虑一下您是否仍然像现在一样喜欢它们)
我正在尝试优化以下查询,根据执行计划,内部查询中的排序成本很高。是否可以重写以下查询以使其易于阅读和执行良好?
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。
在我们正确回答这个问题之前,您需要解决一些问题。
- 确保查询按原样工作。由于
payvoucher.claimid
,您提供给我们的版本将无法编译。我们可以猜到它应该是什么,但是当它被证明是不同的东西时,再努力也没有用。 - 你可能 运行 这在不区分大小写的环境中,它可能会在那里工作,但通常你应该尽量保持你的 table,字段,变量名称 'case-consequent' . (作为 .NET 从业者,这应该是第二天性 =)
- table-定义、索引和对所涉及记录数量的估计以及数据交互方式(如果可能)的估计会有所帮助。 ..)
- 如果您能告诉我们您的 期望 以及这些 table 上的其他流程以及我们的解决方案对这些的影响有多严重,将会有额外的好处。 (我们可能可以使 SELECT 超快,但代价是使 INSERT/UPDATE/DELETE 相当慢)
(最后,摆脱 NOLOCK
提示,或将它们更改为同义词 READUNCOMMITTED
并考虑一下您是否仍然像现在一样喜欢它们)