Proper way to handle 'optional' where clause filters in SQL?

前端 未结 5 1177
广开言路
广开言路 2020-12-31 23:12

Let\'s say you have a stored procedure, and it takes an optional parameter. You want to use this optional parameter in the SQL query. Typically this is how I\'ve seen it don

5条回答
  •  梦谈多话
    2020-12-31 23:53

    EDIT: Adding link to similar question/answer with context as to why the union / if...else approach works better than OR logic (FYI, Remus, the answerer in this link, used to work on the SQL Server team developing service broker and other technologies)

    Change from using the "or" syntax to a union approach, you'll see 2 seeks that should keep your logical read count as low as possible:

    SELECT * FROM dbo.MyTableName t1
    WHERE t1.ThisField = 'test'
    AND @MyOptionalParam IS NULL 
    union all
    SELECT * FROM dbo.MyTableName t1
    WHERE t1.ThisField = 'test'
    AND t1.MyField = @MyOptionalParam
    

    If you want to de-duplicate the results, use a "union" instead of "union all".

    EDIT: Demo showing that the optimizer is smart enough to rule out scan with a null variable value in UNION:

    if object_id('tempdb..#data') > 0
        drop table #data
    go
    
    -- Put in some data
    select  top 1000000
            cast(a.name as varchar(100)) as thisField, cast(newid() as varchar(50)) as myField
    into    #data
    from    sys.columns a
    cross join sys.columns b
    cross join sys.columns c;
    go
    
    -- Shwo count
    select count(*) from #data;
    go
    
    -- Index on thisField
    create clustered index ixc__blah__temp on #data (thisField);
    go
    
    set statistics io on;
    go
    
    -- Query with a null parameter value
    declare @MyOptionalParam varchar(50);
    select  *
    from    #data d 
    where   d.thisField = 'test'
    and     @MyOptionalParam is null;
    go
    
    -- Union query
    declare @MyOptionalParam varchar(50);
    select  *
    from    #data d 
    where   d.thisField = 'test'
    and     @MyOptionalParam is null
    union all
    select  *
    from    #data d 
    where   d.thisField = 'test'
    and     d.myField = '5D25E9F8-EA23-47EE-A954-9D290908EE3E';
    go
    
    -- Union query with value
    declare @MyOptionalParam varchar(50);
    select @MyOptionalParam = '5D25E9F8-EA23-47EE-A954-9D290908EE3E'
    select  *
    from    #data d 
    where   d.thisField = 'test'
    and     @MyOptionalParam is null
    union all
    select  *
    from    #data d 
    where   d.thisField = 'test'
    and     d.myField = '5D25E9F8-EA23-47EE-A954-9D290908EE3E';
    go
    
    if object_id('tempdb..#data') > 0
        drop table #data
    go
    

提交回复
热议问题