[SQL]動態查詢條件(Catch-all Queries)

很多系統的查詢畫面之中,常常會有許多的非必填的查詢條件,
有輸入值就依那個資料查詢,沒有就怱略那個查詢條件。
針對這樣子的SQL,會造成 SQL Optimizer 無法正確判斷回傳的資料筆數。

很多系統的查詢畫面之中,常常會有許多的非必填的查詢條件,

有輸入值就依那個資料查詢,沒有就怱略那個查詢條件。

在「篩選條件多加 OR 來減少程式中 if 的判斷」這篇是針對程式不使用if。

然而針對這樣子的SQL,會造成 SQL Optimizer 無法正確判斷回傳的資料筆數,

而選擇到不好的執行計畫去執行。

針對這樣子的問題,有許多討論的文章,如下,

Catch-all queries

Dynamic Search Conditions in T-SQL (Version for SQL 2005 and Earlier)

Dynamic Search Conditions in T-SQL (Version for SQL 2008 (SP1 CU5 and later))

The Tipping Point Query Answers

 

To be, or not to be

解法就是用 if 或是用 OR + OPTION(RECOMPILE) ,測試如下,

-- drop table TBLA
-- GO
-- Step 1: Create Table
CREATE TABLE TBLA
(
c1 INT,
c2 VARCHAR(30)
);
GO

-- Step 2: Create NoCluster Index
CREATE NONCLUSTERED INDEX NIDX_TBLA_C2
ON dbo.TBLA
(
c2
);

GO

-- Step 3: insert data
DECLARE @I INT;
SET @I = 0;
WHILE @I < 100
BEGIN
 SET @I = @I + 1;
 INSERT dbo.TBLA( c1, c2 ) VALUES (@I, LTRIM(STR(@I)));
END
 
GO

-- Step 4: turn on include actual execution plan 
-- Step 5: compare 3 query  ]

-- Query 1: index seek
DECLARE @c2 VARCHAR(30);
SET @c2 = '66';
SELECT c2
    FROM TBLA
    WHERE (c2 =@c2);
 
-- Query 2: index scan
SELECT c2
    FROM TBLA
    WHERE (c2 = @c2 OR @c2 = '');

-- Query 3: index seek
SELECT c2
    FROM TBLA
    WHERE (c2 = @c2 OR '66' = '' );

image

雖然 Query 2 使用 index scan ,因為資料小,所以 I/O 是一樣的,如下,

(1 row(s) affected)
Table 'TBLA'. Scan count 1, logical reads 2, physical reads 0

(1 row(s) affected)
Table 'TBLA'. Scan count 1, logical reads 2, physical reads 0

(1 row(s) affected)
Table 'TBLA'. Scan count 1, logical reads 2, physical reads 0

 

再加一些資料,然後比較4個Query,如下,

-- Step 6: turn off include actual execution plan 
-- Step 7: Insert more data
DECLARE @I INT;
SET @I = 10;
WHILE @I < 3000
BEGIN
	SET @I = @I + 1;
	INSERT dbo.TBLA( c1, c2 ) VALUES (@I, LTRIM(STR(@I)));
END
 
GO

-- Step 8: turn on include actual execution plan 
-- Step 9: compare 4 query  
DECLARE @c2 VARCHAR(30);
SET @c2 = '66';
SELECT c2
    FROM TBLA
    WHERE (c2 =@c2);

SELECT c2
    FROM TBLA
    WHERE (c2 = @c2 OR @c2 = '');

SELECT c2
    FROM TBLA
    WHERE (c2 = @c2 OR '66' = '' );

SELECT c2
    FROM TBLA
    WHERE (c2 = @c2 OR @c2 = '') OPTION(RECOMPILE);

-- Step 10: turn off include actual execution plan 
-- Step 11: SET STATISTICS IO ON
SET STATISTICS IO ON;
-- Step 12: compare 4 query  
DECLARE @c2 VARCHAR(30);
SET @c2 = '66';
SELECT c2
    FROM TBLA
    WHERE (c2 =@c2);

SELECT c2
    FROM TBLA
    WHERE (c2 = @c2 OR @c2 = '');

SELECT c2
    FROM TBLA
    WHERE (c2 = @c2 OR '66' = '' );

SELECT c2
    FROM TBLA
    WHERE (c2 = @c2 OR @c2 = '') OPTION(RECOMPILE);

image

 

資料量一大,I/O就有差別了哦! 如下,

(2 row(s) affected)  WHERE (c2 =@c2);
Table 'TBLA'. Scan count 1, logical reads 2

(2 row(s) affected) c2 = @c2 OR @c2 = '');
Table 'TBLA'. Scan count 1, logical reads 14

(2 row(s) affected) (c2 = @c2 OR '66' = '' );
Table 'TBLA'. Scan count 1, logical reads 2

(2 row(s) affected) (c2 = @c2 OR @c2 = '') OPTION(RECOMPILE);
Table 'TBLA'. Scan count 1, logical reads 2

 

以上針對 變數 + OR 的狀況,記錄下來,謝謝大家。

 

*** 請注意: 在程式維護性與SQL的執行效能之間,就請大家依實際的需求進行選擇。 ***

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^