<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          我欠 SQL Server 優(yōu)化器一個道歉

          共 4087字,需瀏覽 9分鐘

           ·

          2021-02-17 08:19

          點擊藍色“有關(guān)SQL”關(guān)注我喲

          加個“星標”,天天與10000人一起快樂成長

          圖 |Lenis


          前天的文章,寫了 SQL Server 的 Predicate Pushdown.

          SQL Server 的弱雞 Predicate Pushdown

          舉例想說明的是,子查詢能不能接收外層的條件判斷。

          文中的例子沒有舉好,誤判了 SQL Server 不能進行 Predicate Pushdown, 實際上它的優(yōu)化器總是進行謂詞下推。

          下面在原來的基礎(chǔ)上,再做一遍實驗與說明。

          上例子,以 AdventureWorks2016 數(shù)據(jù)庫為背景,完成一段帶子查詢的SQL:



          ??SELECT?Prod.Name?AS?ProductName,?Sales.ModifiedDate
          ??FROM?Production.Product?Prod??
          ??INNER?JOIN?(
          ???SELECT?TOP?10?ProductID,?ModifiedDate,OrderQty
          ???FROM?Sales.SalesOrderDetail?Detail?
          ???ORDER?BY?ProductID,ModifiedDate?DESC?
          ??)?Sales?
          ?on?Sales.ProductID?=?Prod.ProductID?
          ?WHERE?Sales.ModifiedDate?BETWEEN?'2011-01-01'?AND?'2011-10-01'?AND?Sales.OrderQty=?2?


          例子中,涉及到的兩張表,結(jié)構(gòu)與索引如下:


          --表結(jié)構(gòu)

          CREATE?TABLE?[Production].[Product](
          ?[ProductID]?[int]?IDENTITY(1,1)?NOT?NULL,
          ?[Name]?[dbo].[Name]?NOT?NULL,
          ?[ProductNumber]?[nvarchar](25)?NOT?NULL,
          ?...
          ?[DiscontinuedDate]?[datetime]?NULL,
          ?[rowguid]?[uniqueidentifier]?ROWGUIDCOL??NOT?NULL,
          ?[ModifiedDate]?[datetime]?NOT?NULL,
          ?CONSTRAINT?[PK_Product_ProductID]?PRIMARY?KEY?CLUSTERED?
          (
          ?[ProductID]?ASC
          )?
          )?ON?[PRIMARY]

          GO?



          CREATE?TABLE?[Sales].[SalesOrderDetail](
          ?[SalesOrderID]?[int]?NOT?NULL,
          ?[SalesOrderDetailID]?[int]?IDENTITY(1,1)?NOT?NULL,
          ?[CarrierTrackingNumber]?[nvarchar](25)?NULL,
          ?[OrderQty]?[smallint]?NOT?NULL,
          ?[ProductID]?[int]?NOT?NULL,
          ?[SpecialOfferID]?[int]?NOT?NULL,
          ?[UnitPrice]?[money]?NOT?NULL,
          ?[UnitPriceDiscount]?[money]?NOT?NULL,
          ?[LineTotal]??AS?(isnull(([UnitPrice]*((1.0)-[UnitPriceDiscount]))*[OrderQty],(0.0))),
          ?[rowguid]?[uniqueidentifier]?ROWGUIDCOL??NOT?NULL,
          ?[ModifiedDate]?[datetime]?NOT?NULL,
          ?CONSTRAINT?[PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID]?PRIMARY?KEY?CLUSTERED?
          (
          ?[SalesOrderID]?ASC,
          ?[SalesOrderDetailID]?ASC
          )WITH?(PAD_INDEX?=?OFF,?STATISTICS_NORECOMPUTE?=?OFF,?IGNORE_DUP_KEY?=?OFF,?ALLOW_ROW_LOCKS?=?ON,?ALLOW_PAGE_LOCKS?=?ON)?ON?[PRIMARY]
          )?ON?[PRIMARY]
          GO


          --索引:

          ALTER?TABLE?[Sales].[SalesOrderDetail]?ADD??CONSTRAINT?[PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID]?PRIMARY?KEY?CLUSTERED?
          (
          ?[SalesOrderID]?ASC,
          ?[SalesOrderDetailID]?ASC
          )?
          GO

          CREATE?NONCLUSTERED?INDEX?[IDX_MOD_QTY_PROD_1]?ON?[Sales].[SalesOrderDetail]
          (
          ?[ModifiedDate]?ASC
          )
          INCLUDE?(?[OrderQty],?[ProductID])?
          ?
          ?
          GO

          ?


          這段 SQL 里,子查詢有了 Top N 這樣的邏輯,外層明明有條件篩選,也是不能下推到子查詢的. 因為這 Top ?N 就是要依據(jù) ModifiedDate 來判斷的。而外層的 ModifiedDate 的條件篩選,正好與之沖突。

          image

          從它的執(zhí)行計劃,可以看到,filter這一層已經(jīng)放到子查詢后面(見圖左邊);子查詢使用了 index scan,沒有做任何的條件篩選(Predicate).

          同樣,衍生下子查詢:



          ?
          ??SELECT?Prod.Name?AS?ProductName,?Sales.ModifiedDate
          ??FROM?Production.Product?Prod??
          ??INNER?JOIN?(
          ???SELECT?TOP?10?ProductID,?ModifiedDate,OrderQty
          ???FROM?Sales.SalesOrderDetail?Detail?
          ???WHERE?Detail.ModifiedDate?BETWEEN?'2011-01-01'?AND?'2011-10-01'??
          ???ORDER?BY?ProductID,ModifiedDate?DESC?
          ??)?Sales?
          ?on?Sales.ProductID?=?Prod.ProductID?
          ?WHERE??Sales.OrderQty=?2?

          當我們留 OrderQty = 2 在外層查詢,那么它也是不能被下推到子查詢:

          image

          而什么樣的條件下,外層的條件篩選,能夠下推到子查詢呢?

          ?SELECT?Prod.Name?AS?ProductName,?Sales.ModifiedDate
          ?FROM?Production.Product?Prod?
          ?INNER?JOIN?(
          ???SELECT?ProductID,ModifiedDate,OrderQty,ROW_NUMBER()OVER(PARTITION?BY?ProductID,OrderQty?ORDER?BY?ModifiedDate?DESC)?AS?RNK
          ???FROM?Sales.SalesOrderDetail?Detail?
          ???)?Sales?
          ??ON?Sales.ProductID?=?Prod.ProductID?
          WHERE?Sales.ModifiedDate?BETWEEN?'2011-01-01'?AND?'2011-10-01'??
          ?and?Sales.OrderQty?=?2?

          像這樣的,子查詢沒有篩檢數(shù)據(jù),就可以。

          image
          image

          稍稍要注意的是,seek predicate 與 predicate 區(qū)別。

          Seek Predicate 是索引訪問方式,這里的索引,以ModifiedDate作為鍵。OrderQty, ProductID放在葉子節(jié)點。

          Predicate 是篩選條件, OrderQty 不能作為 SARG 鍵,所以只做篩選。

          SARG: Search Argument-able

          仔細研究 Predicate,? 跟在子查詢外,就是 Filter ,說明沒有被下推(Pushdown); 而用在子查詢內(nèi),在索引上做了條件篩選,說明被下推了。

          再來個更復(fù)雜的下推:



          ?SELECT?Prod.Name?AS?ProductName,?Sales.ModifiedDate
          ?FROM?Production.Product?Prod?
          ?INNER?JOIN?(

          ???
          ??SELECT?ProductID,ModifiedDate,OrderQty?AS?Quantity,ROW_NUMBER()OVER(PARTITION?BY?ProductID,OrderQty?ORDER?BY?ModifiedDate?DESC)?AS?RNK
          ??FROM?Sales.SalesOrderDetail?Detail?
          ???

          ??UNION?ALL?

          ??SELECT?*?
          ??FROM?(
          ???SELECT?ProductID,ModifiedDate,OrderQty?AS?Quantity,ROW_NUMBER()OVER(PARTITION?BY?ProductID,OrderQty?ORDER?BY?ModifiedDate?ASC)?AS?RNK
          ???FROM?Sales.SalesOrderDetail?Detail?
          ??)BTM2?
          ??WHERE?BTM2.RNK<=2




          ???)?Sales?
          ??ON?Sales.ProductID?=?Prod.ProductID?
          WHERE?Sales.ModifiedDate?BETWEEN?'2011-01-01'?AND?'2011-10-01'??
          ?and?Sales.Quantity?=?2?

          針對子查詢上半部分:


          SELECT?ProductID,ModifiedDate,OrderQty?AS?Quantity,ROW_NUMBER()OVER(PARTITION?BY?ProductID,OrderQty?ORDER?BY?ModifiedDate?DESC)?AS?RNK
          ??FROM?Sales.SalesOrderDetail?Detail?

          優(yōu)化器做了Predicate 下推,是可以理解的。

          但優(yōu)化器對下半部分,也做了下推,但沒有全推,而是只推了 Quantity =2. 雖然子查詢的列名換了,但依舊還是被優(yōu)化器識別到了.

          image

          但 ModifiedDate 就不能被下推了,只能跟在子查詢后面做 Filter.

          嗯,SQL Server Predicate Pushdown 很智能,也很強大。




          --完--





          往期精彩:


          本號精華合集(三)

          如何寫好 5000 行的 SQL 代碼

          如何提高閱讀 SQL 源代碼的快感

          我在面試數(shù)據(jù)庫工程師候選人時,常問的一些題

          零基礎(chǔ) SQL 數(shù)據(jù)庫小白,從入門到精通的學(xué)習路線與書單










          瀏覽 37
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操逼影音 | 欧日韩黄色视频 | 日韩精品视频网 | 亚洲欧洲视频在线 | 欧美成人无码一区二区三区 |