【问题标题】:event falls on start date of one event and end date of another事件发生在一个事件的开始日期和另一个事件的结束日期
【发布时间】:2026-01-18 11:00:02
【问题描述】:

我有两个sql表

declare @events table (id int, [event date] date)

declare @ranges table (id int, [start date] date, [end date] date)

我正在尝试使用以下查询将事件与范围匹配

select 
    e.id as eid, r.id as rid
from 
    @events as e
inner join 
    @ranges as r on (e.[event date] between r.[start date] and r.[end date]);

我遇到的问题是,如果事件日期是一个事件的结束日期和另一个事件的开始日期,它将在查询结果中显示两次。该事件应仅匹配一个事件。例如

insert into @events values(1, '2014-01-02'); --date falls on end date on one event and start date of another event

insert into @ranges values(2, '2014-01-01','2014-01-02'),(3,'2014-01-02','2014-01-04');`  

我得到以下结果

eid  | rid
1        2 
1        3

如何让它只匹配一个范围而忽略另一个(最好匹配较早的日期范围)?

【问题讨论】:

  • 一个便宜的修复方法是 Select distinct * from .......
  • @happs 仍然返回相同的结果

标签: sql


【解决方案1】:

由于@GPicazos 的回答不包括您可以在稍后添加ranges 表中的较早start_date 的情况(更大的ranges.id),因为ranges.id 不需要确定您的值存储在 date 列中,我将发布此答案。

我只会向您展示如何处理ranges 表中匹配超过 1 个的行。这基本上就是你所问的。我将后一种情况(只有 1 个匹配项)留给您作为练习,因为它不涉及复杂的查询。

我们从您放在cte_events 中的查询开始。

然后,下一个 CTE events_multiple_matches 仅输出具有多个匹配项的 event_id's

下一步 - events_first_date_rangeranges 表中获取最小值 start_date。在这里,我假设您在 ranges 表中没有超过 1 行具有相同的 start_date

由于我们有一个来自上一步的ranges 表的start_date,我们可以再次连接到ranges 表以检索ranges_id 和(如果需要)其他列-start_dateend_date .

希望这会有所帮助。在代码下方,我在您的示例中附加了一个 SQL 小提琴。

WITH cte_events AS (
SELECT
  e.id AS event_id,
  e.event_date
FROM
  events e
  INNER JOIN ranges r ON
    e.event_date BETWEEN r.start_date AND r.end_date
)
, events_multiple_matches AS (
SELECT
  event_id
FROM
  cte_events
GROUP BY event_id
HAVING COUNT(*) > 1
)
, events_first_date_range AS (
SELECT
  e.event_id,
  MIN(r.start_date) AS min_start_date
FROM
  cte_events e
  INNER JOIN events_multiple_matches em ON e.event_id = em.event_id
  INNER JOIN ranges r ON e.event_date BETWEEN r.start_date AND r.end_date
GROUP BY
  e.event_id
)
SELECT
  e.event_id,
  r.id AS ranges_id,
  r.start_date,
  r.end_date
FROM
  events_first_date_range e
  INNER JOIN ranges r ON e.min_start_date = r.start_date

这是一个SQL fiddle,向您展示它是如何工作的。

【讨论】:

  • 这回答了这个问题。谢谢@considerMe
【解决方案2】:

假设日期范围是按升序输入的,下面的查询应该可以工作。如果不是,则不能保证您会得到与开始日期而不是结束日期匹配的那个。

select orig.eid, min(orig.rid)
from (
    select e.id as eid, r.id as rid
    from  @events as e
        inner join @ranges as r
            on (e.[event date] between r.[start date] and r.[end date])
) as orig
group by orig.eid

【讨论】: