T-SQL之變量導致索引無效


T-SQL之變量導致索引無效

 (一)問題提出

1,在開發中是否遇到一個情況,就是在where后寫明具體值時可以用到索引,使用變量時卻不行了呢?

2,是否開始懷疑MS SQL 出現了編譯問題。

(二)測試過程

1,建立測試數據

CREATE TABLE t_order (
  orderid     INT   IDENTITY ( 1 , 1 )   PRIMARY KEY,
  ordertime   DATETIME,
  productname VARCHAR(50))
GO
--創建索引
CREATE INDEX idx_ordertime ON t_order (
      ordertime)
GO
--插入1000000條記錄
WITH cte
     AS (SELECT NUMBER + 1 AS NUMBER
         FROM   master..spt_values a
         WHERE  a.TYPE = 'P'
                AND NUMBER < 1000)
INSERT INTO t_order
           (ordertime,
            productname)
SELECT Getdate() - a.NUMBER,
       LEFT(Newid(),10)
FROM   cte a
       CROSS JOIN cte b
GO

 

2,分別查詢

SET STATISTICS io  ON
--查詢一采用變量
DECLARE  @date DATETIME
SET @date = Getdate()
SELECT *
FROM   t_order
WHERE  ordertime > @date
GO
--查詢二采用變量給出具體值
SELECT *
FROM   t_order
WHERE  ordertime > Getdate()

 3,對比執行計划發現相差太太太太大了。

查詢1掃描了整個表,查詢2確實很好的一個seek加Look up

(三) 原因分析以及驗證

1,原因分析

因為當你使用變量時,查詢語句在編譯時,並不做SET操作。換句話說,即是SET操作是編譯完成后,執行的時候才執行。所以編譯的時候MS SQL 並不知道◎date的值,所以不能產生一個正確的執行計划。

2,驗證

MS SQL在這種情況總按照一個固定的估計值在產生執行計划(即30%),所以做一個全表掃描更划算。讓我們來論證一下,我們對該表插入了1000000條記錄,按照30% ,所以預估行數就該是300000,查看執行計划,果然如此(注意紅色方框):

 (四)解決方案

解決方案1:(使用option(RECOMPILE),在執行時重新編譯):

declare @date datetime
set @date=GETDATE()
select * from T_order where ordertime>@date
option(RECOMPILE)

 解決方案2:給定一個參數提示給該查詢

declare @date datetime
set @date=GETDATE()
select * from T_order where ordertime>@date
option(OPTIMIZE FOR (@date='2012-04-29'))

  解決方案3:封裝成存儲過程,有人就會疑問了,為什么存儲過程可以呢?在這里大家別把參數和變量混淆了,在SQL SERVER里面寫法都一樣,但意義不完全一樣。存儲過程的編譯實在第一次執行的時候才產生執行計划。

--創建存儲過程
CREATE PROC Sp_select_t_order
           @date DATETIME
AS
  SELECT *
  FROM   t_order
  WHERE  ordertime > @date
GO
--執行存儲過程
DECLARE  @date DATETIME
SET @date = Getdate()
EXEC Sp_select_t_order @date

 解決方案4:參數化查詢

sp_executesql
N'select * from T_order where ordertime>@date',
N'@date datetime',
@date='2012-04-29'

 

以上四種解決方案的執行計划都如下,實際環境推薦封裝成存儲過程:

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM