在連結伺服器上使用視圖時出錯
我有這個查詢:
SELECT - FROM -.dbo.tb1 WHERE -IN ( SELECT CASE WHEN EXISTS ( SELECT * FROM -.dbo.tb2U1 WITH (NOLOCK) WHERE col1 = '8614' ) THEN ( SELECT TOP (1) CASE WHEN ISNULL(col2,0) IN (1,2,3,6,8) THEN '2' ELSE '1' END AS col3 FROM -.dbo.tb1U1 WITH (NOLOCK) LEFT join -.dbo.tb3U2 WITH (NOLOCK) ON U1.-= U2.- WHERE -= '8614' ORDER by -DESC ) ELSE '3' END );
當我執行它時
-
,沒關係。它返回了我想要的東西:col2 -------------------------------------------------- Value
但是,如果我在上面執行它
-
(我們有相同的數據庫-
,但沒有表,只有視圖重定向到-
具有相同名稱的表),它會向我顯示此錯誤:Msg 8180, Level 16, State 1, Line 1 Statement(s) could not be prepared. Msg 4145, Level 15, State 1, Line 1 An expression of non-boolean type specified in a context where a condition is expected, near 'THEN'.
連結伺服器的視圖具有以下形式:
SELECT * FROM -.-.dbo.tb2
SELECT
如果我在沒有此處的情況下編寫查詢:...Where -in ( ***NO SELECT HERE*** Case When Exists (Select * from -.dbo...
…它執行完美。
我只是想知道為什麼會這樣。這怎麼可能?這是同一個查詢。
這是一個錯誤,儘管僅在非常特定的情況下才會出現。
SQL Server 使用稱為分佈式查詢 (DQ) 的組件來建構 T-SQL 命令以針對連結伺服器執行。優化器將
CASE
包含子選擇的表達式轉換為在本地對像上執行良好的形式,但不能正確地轉換為遠端查詢形式(在這種特殊情況下)。該錯誤導致生成需要本地計算結果的遠端節點。創建一個內部查詢樹,期望這些計算值作為參數,但不生成任何參數。僅當子選擇與外部查詢不相關時才會出現此錯誤(如這裡的情況),因為應用了僅對本地查詢安全的簡化。
DQ 生成帶有參數的遠端查詢(使用標準
?
語法),但沒有傳遞任何參數,從而導致"statement cannot be prepared"
錯誤(準備好的語句是參數化語句的技術名稱)。如果子查詢包含外部引用(相關)而不是文字值,則不會應用僅限本地的簡化,並且查詢樹將是正確的。
再生產
創建
SQL2008
到另一個 SQL Server 2008 實例的連結伺服器,其中包含AdventureWorks
範例數據庫。在本地實例上創建以下視圖:CREATE VIEW dbo.Product AS SELECT * FROM SQL2008.AdventureWorks.Production.Product AS P; GO CREATE VIEW dbo.TransactionHistory AS SELECT * FROM SQL2008.AdventureWorks.Production.TransactionHistory AS TH;
為以下查詢生成一個估計的執行計劃(當然,執行它只會產生錯誤消息):
SELECT P.ProductID FROM dbo.Product AS P WHERE P.ProductID IN ( SELECT -- Comment this CASE WHEN EXISTS ( SELECT * FROM dbo.TransactionHistory AS TH WHERE TH.ProductID = 1 -- Not correlated ) THEN ( SELECT TOP (1) TH.TransactionID FROM dbo.TransactionHistory AS TH WHERE -- Not correlated TH.ProductID = 1 ORDER BY TH.TransactionID DESC ) ELSE 0 END );
下面的估計查詢計劃突出顯示了問題運算符:
該
Remote Query
運算符的屬性是:SELECT "Tbl1001"."ProductID" "Col1019" FROM "AdventureWorks"."Production"."Product" "Tbl1001" WHERE CASE WHEN ? THEN ? ELSE (0) END = "Tbl1001"."ProductID"
注意
?
參數標記。當遠端伺服器嘗試編譯時,未解析的參數會生成第二條錯誤消息WHEN ? THEN ?
。如果您註釋掉標記
SELECT
的 ,則不會應用有問題的轉換,並且不會生成錯誤的遠端查詢。同樣,例如,用相關性替換任一
TH.ProductID = 1
實例TH.ProductID = P.ProductID
也會生成正確的遠端查詢。此錯誤在 SQL Server 的所有目前版本中重現,包括 SQL Server 2014 RTM CU4。