Sql-Server

當我在兩個索引視圖上通過聚集索引進行內部聯接時,為什麼查詢處理器不能生成帶有 FORCESEEK 提示的查詢計劃?

  • December 12, 2019

當我嘗試將 FORCESEEK 提示與以下查詢一起使用時,出現以下錯誤:

消息 8622,級別 16,狀態 1,第 96 行 由於此查詢中定義的提示,查詢處理器無法生成查詢計劃。在不指定任何提示且不使用 SET FORCEPLAN 的情況下重新送出查詢。

SELECT A.IndexedField1
INTO #TEMP
FROM dbo.IndexedView1 AS A WITH (FORCESEEK)
INNER JOIN dbo.IndexedView2 AS B
   ON A.IndexedField1 = B.IndexedField1
   AND A.IndexedField2 = B.IndexedField2

IndexedView1 和IndexedView2 都是模式綁定的索引視圖,並且在兩個視圖的IndexedField1、IndexedField2 欄位上都有一個唯一的聚集索引。不確定是否相關,但聚集索引有 40 字節大(每行)。

下面是兩個視圖在引擎蓋下的樣子的範例:

SELECT 
   IndexedField1,
   RIGHT(CONVERT(VARCHAR(34), HASHBYTES('MD5', OtherField1 + '||' OtherField2)), 32) AS IndexedField2
FROM dbo.Table1

沒什麼太花哨的,視圖查詢中也沒有提示。

這是估計的執行計劃(不嘗試使用查詢提示): 預計執行計劃

這是一個複製品。據記錄,您不能在沒有NOEXPAND的索引視圖上使用 FORCESEEK 。

例如

drop table if exists table1
drop table if exists table2
go
create table Table1(IndexedField1 int, OtherField1 nvarchar(200), OtherField2 nvarchar(200))
create table Table2(IndexedField1 int, OtherField1 nvarchar(200), OtherField2 nvarchar(200))

go

create view IndexedView1
with schemabinding
as
SELECT 
   IndexedField1,
   RIGHT(CONVERT(VARCHAR(34), HASHBYTES('MD5', OtherField1 + '||' + OtherField2)), 32) AS IndexedField2
FROM dbo.Table1

go

create view IndexedView2
with schemabinding
as
SELECT 
   IndexedField1,
   RIGHT(CONVERT(VARCHAR(34), HASHBYTES('MD5', OtherField1 + '||' + OtherField2)), 32) AS IndexedField2
FROM dbo.Table2

go

create unique clustered index pk_IndexedView1 on IndexedView1(IndexedField1,IndexedField2)
create unique clustered index pk_IndexedView2 on IndexedView2(IndexedField1,IndexedField2)

go

SELECT A.IndexedField1
INTO #TEMP
FROM dbo.IndexedView1 AS A WITH (FORCESEEK)
INNER JOIN dbo.IndexedView2 AS B
   ON A.IndexedField1 = B.IndexedField1
   AND A.IndexedField2 = B.IndexedField2

   /*
Msg 8622, Level 16, State 1, Line 34
Query processor could not produce a query plan because of the hints defined in this query. Resubmit the query without specifying any hints and without using SET FORCEPLAN.
*/
go
drop table if exists #TEMP

SELECT A.IndexedField1
INTO #TEMP
FROM dbo.IndexedView1  AS A with (noexpand)
INNER JOIN dbo.IndexedView2 AS B with (noexpand)
   ON A.IndexedField1 = B.IndexedField1
   AND A.IndexedField2 = B.IndexedField2

執行計劃:

在此處輸入圖像描述

在應用 NOEXPAND 並指定計劃應該只涉及視圖索引之後,您可以使用 FORCESEEK 提示,或者根據需要指定連接樣式(MERGE、LOOP 或 HASH)。

例如

SELECT A.IndexedField1
INTO #TEMP
FROM dbo.IndexedView1 AS A WITH (NOEXPAND)
INNER MERGE JOIN dbo.IndexedView2 AS B
   ON A.IndexedField1 = B.IndexedField1
   AND A.IndexedField2 = B.IndexedField2

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