我会以完全不同的方式处理这个问题并使用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 在编译时不知道@Param1 和@Param2 会是什么,所以它不知道会满足哪个条件,所以无法进行相应的优化,所以为两种情况创建相同的计划
EXECUTE dbo.Proc1 1, 1;
EXECUTE dbo.Proc1 0, 1;
然而,如果您使用IF/ELSE
SQL-Server 可以为每个条件创建最佳计划:
EXECUTE dbo.Proc2 1, 1;
EXECUTE dbo.Proc2 0, 1;
在这种情况下,实际影响并不像查询计划所建议的那么糟糕,因为 SQL-Server 在运行时足够聪明,不会评估 subuqery 以从 T2 if 中选择@Param1 = 0
,而且我并不是说永远不会使用多个OR
条件,但通常当您有一个影响您想要的谓词的常量时,最好用 IF/ELSE 将其分开,而不是将两个谓词混在一起。
有时更少的代码并不总是更有效的查询。
SQL-Fiddle 上的 DDL 和查询