2011-01-19 74 views
4

我有一個巨大的視圖,使用UNION ALL連接了許多查詢,並且每個查詢的第一列都是常量。打開查詢在視圖中運行

例如

CREATE VIEW M AS (
    SELECT 'A' ID, Value FROM A 
    UNION ALL 
    SELECT 'B' ID, Value FROM B 
    ... 
) 

的查詢是在現實中更復雜,但這裏的目的只是爲了在這樣運行了哪些查詢切換:

SELECT * FROM M WHERE ID = 'A' 

執行計劃表明該查詢不匹配ID永不跑步。

我認爲這是一個非常好的功能?我可以用它來結合通過相同的視圖查詢不同但相似的東西。

不過,我結束了一個更好的執行計劃,如果查詢針對CTE這樣的:

WITH M AS (
    SELECT 'A' ID, Value FROM A 
    UNION ALL 
    SELECT 'B' ID, Value FROM B 
    ... 
) 
SELECT * FROM M WHERE ID = 'A' 

這裏的實際查詢的部分樣本:

SELECT CONVERT(char(4), 'T ') EntityTypeID, SystemID, TaskId EntityID 
FROM [dbo].[Task] 

UNION ALL 

SELECT CONVERT(char(4), 'T ') EntityTypeID, s.SystemID, [dbo].[Task].TaskId EntityID 
FROM [dbo].[Task] 
INNER JOIN [dbo].[System] s ON s.MasterSystemID = [dbo].[Task].SystemID 
INNER JOIN SystemEntitySettings ON SystemEntitySettings.SystemID = s.SystemID 
    AND SystemEntitySettings.EntityTypeID = 'T ' 
    AND SystemEntitySettings.IsSystemPrivate = 0 

鑑於如果我運行類似於WHERE EntityTypeiD <> 'T'的東西,它會忽略第一個查詢,但完成第二個查詢(從不返回任何實際的行)。

我遇到的問題,或者說更確切地說,我的問題是,爲什麼它不能完全從視圖中消除查詢,而在CTE情況下這樣做呢?

編輯

我觀察到一些有趣的事情,到目前爲止,我不排除與參數化的交易,但我也可以通過指定查詢提示(顯然任何會做)achive的desiered效果或將第二個連接重寫爲IN謂詞,因爲它只是一個過濾器。

INNER JOIN SystemEntitySettings ON SystemEntitySettings.SystemID = s.SystemID 
    AND SystemEntitySettings.EntityTypeID = 'T ' 
    AND SystemEntitySettings.IsSystemPrivate = 0 

... ...變得

WHERE s.SystemID IN (
     SELECT SystemID 
     FROM dbo.SystemEntitySettings 
     WHERE EntityTypeID = 'T ' AND IsSystemPrivate = 0 
) 

但是,下面的查詢具有同樣的問題。它看起來好像和JOIN操作有關。 (注:與[組]考慮在此查詢的地方附加JOIN)

SELECT CONVERT(char(4), 'CF ') EntityTypeID, s.SystemID, [dbo].[CareerForum].GroupID EntityID 
FROM [dbo].[CareerForum] 
INNER JOIN [dbo].[Group] ON [dbo].[Group].GroupID = [dbo].[CareerForum].GroupID 
INNER JOIN [dbo].[System] s ON s.MasterSystemID = [dbo].[Group].SystemID 
WHERE s.SystemID IN (SELECT SystemID FROM dbo.SystemEntitySettings WHERE EntityTypeID = 'CF ' AND IsSystemPrivate = 0) 

重現

下面的腳本可以用來重現該問題。請注意,執行計劃與查詢提示一起運行時是否完全不同,或者視圖是否使用cte(desiered結果)運行。

CREATE DATABASE test_jan_20 

USE test_jan_20 

create table source (
    x int not null primary key, 
) 

insert into source values (1) 
insert into source values (2) 
insert into source values (3) 
insert into source values (4) 
insert into source values (5) 
insert into source values (6) 

create table other (
    y int not null primary key, 
) 

insert into other values (1) 
insert into other values (2) 
insert into other values (3) 
insert into other values (4) 
insert into other values (5) 
insert into other values (6) 

create view dummy AS (

    SELECT 'A' id, x, NULL y 
    FROM SOURCE 
    WHERE x BETWEEN 1 AND 2 

    UNION ALL 

    SELECT 'B' id, x, NULL y 
    FROM SOURCE 
    WHERE x BETWEEN 3 AND 4 

    UNION ALL 

    SELECT 'B' id, source.x, y 
    FROM SOURCE 
    INNER JOIN other ON y = source.x 
    INNER JOIN source s2 ON s2.x = y - 1 --i need this join for the issue to occur in the execution plan 
    WHERE source.x BETWEEN 5 AND 6 

) 
GO 

--this one fails to remove the JOIN, not OK 
SELECT * FROM dummy WHERE id = 'c' 

--this is OK 
SELECT * FROM dummy WHERE id = 'c' OPTION (HASH JOIN) --NOTE: any query hint seems to do the trick 

--this is OK 
; 
WITH a AS (

    SELECT 'A' id, x, NULL y 
    FROM SOURCE 
    WHERE x BETWEEN 1 AND 2 

    UNION ALL 

    SELECT 'B' id, x, NULL y 
    FROM SOURCE 
    WHERE x BETWEEN 3 AND 4 

    UNION ALL 

    SELECT 'B' id, source.x, y 
    FROM SOURCE 
    INNER JOIN other ON y = source.x 
    INNER JOIN source s2 ON s2.x = y - 1 --i need this join for the issue to occur in the execution plan 
    WHERE source.x BETWEEN 5 AND 6 

) 
SELECT * FROM a WHERE id = 'c' 
+0

是否使用字符串字面量在這兩種情況下或者您使用的參數?如果兩者的字符串文字是否都強制在服務器上打開參數設置? – 2011-01-20 00:07:51

+0

應該選擇「T」的聯合查詢兩個分支? – RichardTheKiwi 2011-01-20 01:20:00

+0

@Martin - 文字不強制參數化。 @cyberkiwi - 這只是整個查詢的一小部分,還有很多其他查詢都是使用UNION ALL連接的,這兩個都應該選擇'T' – 2011-01-20 07:18:27

回答

1

執行計劃表明不上ID匹配從未 運行 查詢。

,因爲你提供一個恆定的「A」,所以該計劃是建立針對特定的字符串「A」,它可以切斷一部分這是正確的。

我有,或者說,我 的問題是,爲什麼它不能完全 從 視圖消除了查詢,當它在熱膨脹係數的情況下這樣做的問題?

我想你剛纔所說的,它沒有?我想你是以參數化的方式使用它,無論是在SP,函數還是參數化查詢中。這導致一個計劃被創造,必須能夠採取各種參數 - 所以切出一部分是不可能的。

達到你想要什麼,你就必須產生動態SQL,將目前的查詢與constant值查詢優化。無論您使用視圖還是內聯表值函數,都是如此。

編輯:下添加重複性

這兩種形式似乎工作以及

select * from (SELECT * FROM dummy) y WHERE id = 'c' 

with a as (Select * from dummy) SELECT * FROM a WHERE id = 'c' 
2

在你的測試案例中,這就是發生了什麼。

對於具有視圖和查詢提示或CTE的查詢,查詢優化器正在使用「矛盾檢測」。您可以在執行計劃屬性中看到OPTIMIZATION LEVELTRIVIAL。產生的微不足道的計劃與this article的第8點所示的計劃完全相同。

對於視圖的查詢,而查詢提示這得到自動參數化。這可以防止在as covered here踢矛盾檢測。

1

有了上次更新,查詢太優化。

如果您提供c(或任何pother遺漏值)作爲過濾器,你將有此計劃:

|--Compute Scalar(DEFINE:([Union1019]=[Expr1018], [Union1020]=[ee].[dbo].[source].[x], [Union1021]=[ee].[dbo].[other].[y])) 
     |--Compute Scalar(DEFINE:([Expr1018]='B')) 
      |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1022])) 
       |--Constant Scan 
       |--Clustered Index Seek(OBJECT:([ee].[dbo].[source].[PK__source__3BD019E5171A1207] AS [s2]), SEEK:([s2].[x]=[Expr1022]) ORDERED FORWARD) 

,隨着不斷的掃描擴大如下:

<RelOp AvgRowSize="19" EstimateCPU="1.57E-07" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="0" LogicalOp="Constant Scan" NodeId="3" Parallel="false" PhysicalOp="Constant Scan" EstimatedTotalSubtreeCost="1.57E-07"> 
    <OutputList> 
    <ColumnReference Database="[ee]" Schema="[dbo]" Table="[source]" Column="x" /> 
    <ColumnReference Database="[ee]" Schema="[dbo]" Table="[other]" Column="y" /> 
    <ColumnReference Column="Expr1022" /> 
    </OutputList> 
    <ConstantScan /> 
</RelOp> 

在其他世界上,source sother o從不被觸摸,這不會產生任何實際輸出,因此,Nested Loops沒有輸入,因此不會執行實際的搜索。

如果將參數替換爲b,您將看到針對所有三個表的更復雜的計劃和實際的JOIN操作。