Jiveman
Jiveman

Reputation: 1072

SQL query with optional parameters in a related table

I have a schema similar to this:

Person
----------------
Id (PK)
Name

EventRegistration
----------------
EventId (PK, FK)
PersonId (PK, FK)
DateRegistered

-- EDIT: Note that a single person can registered for multiple events.

I need to write a query that searches for names of all people who registered for any events that occurred within a provided date range (i.e. DateRegistered should fall within DateRangeStart and DateRangeEnd). The trick is, the input parameters are optional. So you could define both, only start date, only end date, or neither. These would be interpreted, respectively, as "everything within this range," "everything on/after this date," "everything on/before this date," and "all people regardless of any event registrations."

So I came up with something that works, but I was wondering if I could get some help improving this:

-- Assume parameters @DateRangeStart and @DateRangeEnd (both date) are provided by user input

SELECT p.Name
FROM Person p
WHERE 
    (@DateRangeStart IS NULL OR EXISTS (
        SELECT TOP 1 PersonId
        FROM EventRegistration
        WHERE PersonId = p.Id
            AND DateRegistered >= @DateRangeStart
            AND (@DateRangeEnd IS NULL OR DateRegistered <= @DateRangeEnd)
        )
    ) AND
    (@DateRangeEnd IS NULL OR EXISTS (
        SELECT TOP 1 PersonId
        FROM EventRegistration
        WHERE PersonId = p.Id
            AND (@DateRangeStart IS NULL OR DateRegistered >= @DateRangeStart)
            AND DateRegistered <= @DateRangeEnd
        )
    )

Upvotes: 0

Views: 219

Answers (3)

Gordon Bell
Gordon Bell

Reputation: 13633

Something like this should work:

SELECT DISTINCT p.Name
FROM Person p
LEFT JOIN EventRegistration e
ON p.Id = e.PersonId
WHERE (@DateRangeStart IS NULL OR DateRegistered >= @DateRangeStart)
AND (@DateRangeEnd IS NULL OR DateRegistered <= @DateRangeEnd)

Upvotes: 2

Lluis Martinez
Lluis Martinez

Reputation: 1973

Since NULL comparisons do not take advantage of indexes, I would replace the null parameters with valid dates and then use a simple join. It should be fast.

@DateRangeStart = Coalesce(@DateRangeStart , MinDate)
@DateRangeEnd = Coalesce(@DateRangeEnd , MaxDate)

select distinct p.name
from Person p join EventRegistration e on p.personId = e.personId and
     e.DateRegistered  between @DateRangeStart  and @DateRangeEnd 

Another option if you can use dynamic SQL:

query=  select distinct p.name
        from Person p join EventRegistration e 
                  on p.personId = e.personId

if DateRangeStart != null query += 'and DateRegistered >= @DateRangeStart '
if DateRangeEnd  != null query += 'and DateRegistered <= @DateRangeEnd  '

executeQuery(...)

Upvotes: 1

S3S
S3S

Reputation: 25112

A more modified, clean way, would be to boolean the where clause.

declare @DateRangeStart datetime,  @DateRangeEnd datetime
set @DateRangeStart = null
set @DateRangeEnd = null

select 
    Name
    ,Title
    ,DateRegistered
From
    Person p
    left join
        EventRegistration er on
        er.PersonID = p.ID
    left join
        Event e on
        e.id = er.EventID
where
    (er.DateRegistered >= @DateRangeStart or @DateRangeStart is null)
    and
    (er.DateRegistered <= @DateRangeEnd or @DateRangeEnd is null)

Upvotes: 1

Related Questions