私はこれをまったく別の方法でアプローチし、 を使用しますIF/ELSE
。カーディナリティが異なる 2 つの異なる基準を組み合わせると、オプティマイザーが最適なクエリ プランを選択する可能性が低くなります。次のようなものを使用すると、パフォーマンスが大幅に向上します。
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 は、実行時に T2 if から選択するサブクエリを評価しないほど十分にスマートである@Param1 = 0
ためです。OR
ただし、通常、必要な述語に影響を与える定数がある場合は、2 つの述語をマッシュアップするよりも、IF/ELSE で分離するのが最善です。
より少ないコードが常により効率的なクエリであるとは限りません。
SQL-Fiddle での DDL とクエリ