简体   繁体   English

相关表中带有可选参数的SQL查询

[英]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 (ie DateRegistered should fall within DateRangeStart and DateRangeEnd ). 我需要编写一个查询来搜索在给定日期范围内发生的任何事件的所有注册人员的名称(即DateRegistered应该在DateRangeStartDateRangeEnd范围内)。 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
        )
    )

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)

A more modified, clean way, would be to boolean the where clause. 一种更修改,更干净的方法是将where子句布尔化。

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)

Since NULL comparisons do not take advantage of indexes, I would replace the null parameters with valid dates and then use a simple join. 由于NULL比较不利用索引,因此我将用有效日期替换null参数,然后使用简单的联接。 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: 如果可以使用动态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(...)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM