Reputation: 1072
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
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
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
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