Sql-Server

在連結伺服器上使用視圖時出錯

  • January 26, 2021

我有這個查詢:

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。

引用自:https://dba.stackexchange.com/questions/89181