SQL Server存储过程:如果未设置相关参数,则禁用“where”过滤器

时间:2016-02-11 12:17:31

标签: sql sql-server stored-procedures

我得到了一个SQL语句,它使用一些参数来过滤输出。

看起来像这样:

ALTER PROCEDURE [dbo].[spGetPerson] 
        @Vorname varchar(50),
        @Nachname varchar(50),
        @FirmaID int,
        @AbteilungID int,
        @ArbeitsortID int,
        @FunktionID int
AS
BEGIN
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    Select 
        p.Vorname, p.Nachname, ar.Arbeitsort, fi.Firma, 
        ab.Abteilung, fu.Funktion 
    From 
        tblPerson p
    inner join 
        tblArbeitsort ar on ar.ArbeitsortID = p.Arbeitsort_fk
    inner join 
        tblFirmaHatAbteilungUndPersonHatFunktion zt on zt.Person_fk = p.PersonID
    inner join 
        tblFirma fi on fi.FirmaID = zt.Firma_fk
    inner join 
        tblAbteilung ab on ab.AbteilungID = zt.Abteilung_fk
    inner join 
        tblFunktion fu on fu.FunktionID = zt.Funktion_fk
    Where 
        p.Vorname = @Vorname 
        AND p.Nachname = @Nachname 
        AND fi.FirmaID = @FirmaID 
        AND ab.AbteilungID = @AbteilungID 
        AND ar.ArbeitsortID = @ArbeitsortID 
        AND fu.FunktionID = @FunktionID
END

我正在加入一些东西,但这并不重要。

正如您在最后看到的那样,我使用了WHERE子句中的每个参数。我想做的是,如果缺少一个参数,相关的“where”就会消失。

作为示例如果缺少@Nachname,则语句应如下所示:

Where 
    p.Vorname = @Vorname 
    AND fi.FirmaID = @FirmaID 
    AND ab.AbteilungID = @AbteilungID 
    AND ar.ArbeitsortID = @ArbeitsortID 
    AND fu.FunktionID = @FunktionID

我该怎么做?

4 个答案:

答案 0 :(得分:3)

我发现在这些案件中救援的另一种方式如下。它的工作原理很简单。如果required参数不为null,则将使用其他列与自身匹配,从而导致引擎忽略该连接,因为WHERE 1 = 1类型连接被跳过。希望这有帮助

Where 
p.Vorname = COALESCE(@Vorname, p.Vorname)
AND fi.FirmaID = COALESCE(@FirmaID ,fi.FirmaID )
AND ab.AbteilungID = COALESCE(@AbteilungID ,ab.AbteilungID )
AND ar.ArbeitsortID = COALESCE(@ArbeitsortID,ar.ArbeitsortID )
AND fu.FunktionID = COALESCE(@FunktionID,fu.FunktionID)

答案 1 :(得分:2)

执行此操作的典型方法是使用如下逻辑:

Where (p.Vorname = @Vorname or @Vorname is null) AND
      (p.Nachname = @Nachname or @Nachname is null) AND
      (fi.FirmaID = @FirmaID or @FirmaID is null) AND
      (ab.AbteilungID = @AbteilungID or @AbteilungID is null) AND
      (ar.ArbeitsortID = @ArbeitsortID or @ArbeitsortID is null) AND
      (fu.FunktionID = @FunktionID or @FunktionID is null)

一个注意事项:使用此类复杂的where子句会影响查询的优化策略。如果性能是一个大问题(并且您在用于条件的where子句中的列上有索引),那么您可能希望使用动态SQL。也就是说,您将根据具有值的参数构造where子句,而不是在运行时检查NULL

答案 2 :(得分:2)

根据您的表格大小(记录数量)以及填充过滤器的方式(可能您大部分时间只指定了两个过滤器,dynamic SQL可能会带来更好的结果):

DECLARE @SQL NVARCHAR(MAX) = N'
    Select p.Vorname, p.Nachname, ar.Arbeitsort, fi.Firma, ab.Abteilung, fu.Funktion From tblPerson p
    inner join tblArbeitsort ar on ar.ArbeitsortID = p.Arbeitsort_fk
    inner join tblFirmaHatAbteilungUndPersonHatFunktion zt on zt.Person_fk = p.PersonID
    inner join tblFirma fi on fi.FirmaID = zt.Firma_fk
    inner join tblAbteilung ab on ab.AbteilungID = zt.Abteilung_fk
    inner join tblFunktion fu on fu.FunktionID = zt.Funktion_fk
    WHERE 1 = 1'

IF (@Vorname IS NOT NULL) @SQL = @SQL + ' AND p.Vorname = @Vorname'
IF (@Nachname IS NOT NULL) @SQL = @SQL + ' AND p.Nachname = @Nachname'
-- other if conditions come here

EXEC sp_executesql @SQL, N'@Vorname VARCHAR(50), @Nachname VARCHAR(50)', 
   @Vorname = @Vorname, @Nachname = @Nachname

虽然dynamic SQL没有从某些性能改进中受益,但事实上你的WHERE要简单得多(多个OR对性能不友好)可能会带来更好的结果。

答案 3 :(得分:0)

你不能轻易有效地做到这一点,这是一个很好的例子,为什么运行SP查询是一个死胡同。

你能做的最好的事情是:

WHERE ([x] LIKE @x AND @x IS NOT NULL)

与语句上的OPTION RECOMPILE结合使用,以避免参数嗅探以确定可能错误的查询计划。