我会完全不同处理这和使用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