我们的一位开发人员正在尝试在开发服务器上运行以下查询,这涉及从链接服务器生产中提取数据。查询在停止前运行了 14 个多小时。
我查看了 SQL Sentry Plan Explorer 中的执行计划 - 请在下面找到执行计划。
如何调整此查询以获得更好的性能?查询中是否有任何明显的错误?是否有任何指针、博客文章可以帮助我改进此查询?
涉及的两台服务器都是SQL Server 2005
SELECT A.SETID
,A.CUST_ID
,A.CNTCT_SEQ_NUM
,A.NAME1
,A.TITLE
,C.DESCR
FROM PS_CUST_CONTACT A
,[linksrv].[prodDB].dbo.PS_BO_ROLE Z
,[linksrv].[prodDB].dbo.PS_RD_PERSON B
,[linksrv].[prodDB].dbo.PS_BO_ROLE_TYPE C
WHERE Z.BO_ID = B.BO_ID
AND Z.ROLE_TYPE_ID = C.ROLE_TYPE_ID
AND Z.ROLE_END_DT >= GETDATE()
AND A.EFFDT = (
SELECT MAX(EFFDT)
FROM PS_CUST_CONTACT CUST_CONTACT
WHERE CUST_CONTACT.SETID = A.SETID
AND CUST_CONTACT.CUST_ID = A.CUST_ID
AND CUST_CONTACT.CNTCT_SEQ_NUM = A.CNTCT_SEQ_NUM
AND CUST_CONTACT.EFFDT <= { FN CURDATE() }
)
AND A.EFF_STATUS = 'A'
AND B.PERSON_ID IN (
SELECT A1.PERSON_ID
FROM PS_CONTACT A1
,PS_CONTACT_CUST B1
WHERE A1.EFFDT = (
SELECT MAX(A_ED.EFFDT)
FROM PS_CONTACT A_ED
WHERE A1.SETID = A_ED.SETID
AND A1.CONTACT_ID = A_ED.CONTACT_ID
AND A_ED.EFFDT <= SUBSTRING(CONVERT(CHAR, GETDATE(), 121), 1, 10)
)
AND A1.SETID = B1.SETID
AND A1.CONTACT_ID = B1.CONTACT_ID
AND B1.EFFDT = (
SELECT MAX(B_ED.EFFDT)
FROM PS_CONTACT_CUST B_ED
WHERE B1.SETID = B_ED.SETID
AND B1.CONTACT_ID = B_ED.CONTACT_ID
AND B_ED.EFFDT <= A.EFFDT
)
AND A.CNTCT_SEQ_NUM = B1.CNTCT_SEQ_NUM
AND A.SETID = B1.CUSTOMER_SETID
AND A.CUST_ID = B1.CUST_ID
)
为了进一步充实 Aaron 所说的内容,链接服务器的性能,尤其是对于大型结果集、跨服务器连接和跨服务器子查询,通常令人失望。如果您使用 Profiler 观察远程服务器,您可能会发现本地服务器用获取单个行以匹配连接列的请求轰炸远程服务器。发生这种情况时,网络延迟和调用开销会共同影响性能。
如果能不费力地查询本地数据,那最好不过了。您可能能够恢复生产备份或使用 SSIS(甚至 bcp,它仍然有效)将数据从生产服务器复制到本地服务器上的一些工作表。通常 SSIS、bcp 和类似的策略比链接服务器更快,并且可能有助于避免日志文件增长的问题。
如果您必须从远程服务器查询数据,您可能会发现重写查询以便它使用 OPENQUERY()(而不是四部分名称)并将查询的所有“远程部分”“发送”到然后将远程服务器的结果加入到本地数据中会更有效。SQL 应该足够聪明,可以将所有连接移动到远程服务器,但有时它不会,并且 OPENQUERY() 为您提供了一种方法来强制 SQL 执行您想要的操作。
另一个类似的策略是首先运行查询的“远程部分”并将结果放入临时表,(可选)索引临时表,然后将查询的“本地部分”连接到临时表。同样,这可以帮助您强制 SQL 执行它应该执行的操作。
这听起来像是更多的工作,但 SQL 可能能够更有效地运行。与往常一样,注意连接上的数据类型,您的 SARG 或索引将被忽略。