我會完全不同處理這和使用IF/ELSE
。通過將不同基數與不同基數混合在一起,可以降低優化者選擇最佳查詢計劃的機率。你會得到更好的表現使用這樣的事情:
IF @Param = 0
BEGIN
SELECT *
FROM T
WHERE A IN (SELECT TID FROM T2 WHERE ID = 1 @param2);
END
ELSE
BEGIN
SELECT *
FROM T
WHERE ID = @Param1;
END
它看起來像更多的代碼,所以應該效率較低,但實際上不是。使用這個測試場景:
CREATE TABLE T (ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, A INT NOT NULL, B INT NULL);
INSERT T (A, B)
SELECT A, Number
FROM ( SELECT TOP 1000 A = RANK() OVER(ORDER BY a.object_id)
FROM sys.all_objects a
) a
CROSS JOIN (VALUES (1), (2), (3)) n (Number);
CREATE TABLE T2 (ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, TID INT NOT NULL);
INSERT T2 (TID)
SELECT T.ID
FROM T
CROSS JOIN (VALUES (1), (2), (3)) n (Number);
CREATE NONCLUSTERED INDEX IX_T_A ON T (A);
CREATE NONCLUSTERED INDEX IX_T2_TID ON T2 (TID);
GO
CREATE PROCEDURE dbo.Proc1 @Param1 INT, @Param2 INT
AS
SELECT ID, A, B
FROM T
WHERE (@param1 <> 0 AND t.A = @Param1)
OR (@param1 = 0 AND t.A IN(SELECT TID FROM T2 WHERE ID = @param2));
-- (SORRY TIM, BUT YOURS WAS THE BEST OF THE REST)
GO
CREATE PROCEDURE dbo.Proc2 @Param1 INT, @Param2 INT
AS
IF @Param1 = 0
BEGIN
SELECT ID, A, B
FROM T
WHERE A IN (SELECT TID FROM T2 WHERE ID = @param2);
END
ELSE
BEGIN
SELECT ID, A, B
FROM T
WHERE A = @Param1;
END
GO
如果運行的第一個程序(不帶IF),因爲SQL-Server不知道什麼@參數1和@參數2將在編譯的時候,它不知道哪些條件見面會,所以不能相應地優化,所以創造了兩個條件
EXECUTE dbo.Proc1 1, 1;
EXECUTE dbo.Proc1 0, 1;
同樣的計劃反之,如果你使用SQL IF/ELSE
- 服務器可以爲每個條件下的最優方案:
EXECUTE dbo.Proc2 1, 1;
EXECUTE dbo.Proc2 0, 1;
在這種情況下的實際影響並不如查詢計劃建議的那樣糟糕,因爲SQL-Server是運行時不夠聰明,不評估subuqery如果@Param1 = 0
從T2選擇,並我並不是說永遠不會有使用多個條件的情況,但通常情況下,當您有一個影響所需謂詞的常量時,最好將它與IF/ELSE分開,而不是將兩個謂詞混合在一起。
有時較少的代碼並不總是更高效的查詢。
DDL and queries on SQL-Fiddle
這個查詢將無法正常工作。如果tabl10不返回@ param2的任何行怎麼辦? –
如果'@Param2'沒有返回行,那麼除了不工作,即使您不關心結果,當@Param1爲0時,您也會在tbl10上強制進行表掃描。這不是一個有效的解決方案。 – GarethD