查詢調整 - SQL Server
我們的一位開發人員正在嘗試在開發伺服器上執行以下查詢,這涉及從連結伺服器、生產環境中提取數據。查詢執行了超過 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 或索引將被忽略。