Share via


Database Programming: One Last Gasp On Optional Parameters

July 12 update: Everything that follows is WRONG. See Adam's comment and my follow-up.

You might think I'm obsessing over this, and you may well be right, but I wanted to make one more comment about handling optional parameters.

The issue with the syntax I initially proposed was that it didn't handle nullity in the database properly. If any of the tested columns are NULLable, then, as far as our current research is concerned, dynamic SQL is the one, true path.

However, if all of the columns being tested in the database are declared NOT NULL, but the parameters themselves are NULLable, then I believe a modified version of my proposed syntax will work:

SELECT

  x
FROM dbo.foo
WHERE y1 = COALESCE(@p1, y1)
AND y2 = COALESCE(@p2, y2)
AND y3 = COALESCE(@p3, y3)
AND y4 = COALESCE(@p4, y4)

This produces identical results (and an identical query plan) to dynamic SQL if the conditions above are met. It's certainly a niche application rather than the bold generic statement that I initially hoped to make, but if I'm correct (Adam? Tony?) then I will at least have salvaged a small shred of this work.. and my dignity. :)

Comments are encouraged, as always. Thanks for tolerating my focus on this issue.

     -wp

Comments

  • Anonymous
    January 01, 2003
    PingBack from http://blogs.technet.com/wardpond/archive/2006/07/10/441050.aspx
  • Anonymous
    January 01, 2003
    When I first published Pond’s Laws, I promised it would be a living document.  Herewith is the first evidence.
  • Anonymous
    January 01, 2003
    The comment has been removed
  • Anonymous
    January 01, 2003
    PingBack from http://blogs.technet.com/wardpond/archive/2006/07/12/441395.aspx
  • Anonymous
    July 12, 2006
    I hope you're joking, Ward?

    ---
    use tempdb
    go

    select
    y1.number as y1,
    y2.number as y2
    into wardstable
    from
    master..spt_values y1,
    master..spt_values y2
    where
    y1.type = 'P' and y1.number <= 500
    and y2.type = 'P' and y2.number <= 500
    go

    create clustered index y1 on wardstable (y1)
    create nonclustered index y2 on wardstable (y2)
    go

    create procedure GetWardsNumber
    @y1 int = null,
    @y2 int = null
    as
    begin
    set nocount on

    select *
    from wardstable
    where
    y1 = coalesce(@y1, y1)
    and y2 = coalesce(@y2, y2)
    end
    go

    create procedure GetAdamsNumber
    @y1 int = null,
    @y2 int = null
    as
    begin
    set nocount on

    declare @sql nvarchar(max)
    set @sql =
    'select * ' +
    'from wardstable ' +
    'where 1=1 ' +
    case
    when @y1 is not null then
    'and y1 = @y1 '
    else ''
    end +
    case
    when @y2 is not null then
    'and y2 = @y2 '
    else ''
    end

    exec sp_executesql
    @sql,
    N'@y1 int, @y2 int',
    @y1,
    @y2
    end
    go


    set statistics io on
    go

    exec getwardsnumber @y1 = 1
    go

    exec getwardsnumber @y2 = 2
    go

    exec getadamsnumber @y1 = 1
    go

    exec getadamsnumber @y2 = 2
    go

    set statistics io off
    go

    drop proc getadamsnumber
    go
    drop proc getwardsnumber
    go
    drop table wardstable
    go
    ---
  • Anonymous
    July 12, 2006
    Boy, Adam, I can sure see why you ask the question.

    Readers, Adam's syntax in the sample generates 6 logical reads while mine generates 594.

    I hereby renounce all forms of the COALESCE syntax, and furthermore promise not to write any more performance-related blog posts while I'm packing for a vacation.

    Thanks, Adam, for setting me straight.