You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL Server:识别并删除重叠日期范围数据的SQL查询问题

解决日期范围重叠的SELECT查询问题

我完全理解你的困扰——之前用GROUP BY的方式只能揪出完全重复的记录,但没法识别那些日期范围部分重叠或者互相包含的情况,尤其是还要处理Date_To为NULL(表示无结束日期)的场景对吧?下面我给你一步步拆解正确的解决思路和SQL代码:

核心:日期重叠的判断逻辑

首先得明确,两个日期区间[A1, B1]和[A2, B2](其中B可以为NULL,代表无限期)重叠的条件是:

  • 第一个区间的开始日期 ≤ 第二个区间的结束日期(如果第二个区间有结束日期,否则视为永远成立)
  • 第二个区间的开始日期 ≤ 第一个区间的结束日期(如果第一个区间有结束日期,否则视为永远成立)

对于Date_To为NULL的情况,我们可以用一个极晚的日期(比如'9999-12-31')来替代,这样就能统一处理所有场景。

找出所有重叠记录的SQL查询

因为你的测试表没有主键,我们先给每条记录生成唯一行号,避免一条记录和自己匹配,然后通过自连接来筛选重叠记录:

WITH numbered_records AS (
    SELECT 
        Dealer_Number,
        Date_From,
        Date_To,
        -- 给每个经销商的记录按开始日期排序,生成唯一行号
        ROW_NUMBER() OVER (PARTITION BY Dealer_Number ORDER BY Date_From, ISNULL(Date_To, '9999-12-31')) AS row_num
    FROM #tmp
)
SELECT DISTINCT
    t1.Dealer_Number,
    t1.Date_From,
    t1.Date_To
FROM numbered_records t1
JOIN numbered_records t2
    ON t1.Dealer_Number = t2.Dealer_Number
    AND t1.row_num <> t2.row_num -- 排除自己和自己匹配
    -- 核心重叠判断条件
    AND t1.Date_From <= ISNULL(t2.Date_To, '9999-12-31')
    AND t2.Date_From <= ISNULL(t1.Date_To, '9999-12-31')
ORDER BY t1.Dealer_Number, t1.Date_From, ISNULL(t1.Date_To, '9999-12-31');

代码说明:

  1. CTE numbered_records:给每个经销商的记录生成唯一行号,确保我们不会把一条记录和自身比较。
  2. 自连接逻辑:只匹配同一经销商、不同行号的记录,然后用替换NULL的方式统一判断日期重叠。
  3. DISTINCT去重:因为重叠的记录会互相匹配(比如t1匹配t2,t2也匹配t1),用DISTINCT可以得到唯一的重叠记录列表。

删除重叠记录的方案(可选)

如果需要删除重叠记录,只保留符合业务规则的一条(比如优先保留无结束日期的,或者范围最大的),可以用以下SQL:

WITH overlapping_records AS (
    SELECT 
        *,
        -- 按业务规则排序,rank=1的是要保留的记录
        ROW_NUMBER() OVER (
            PARTITION BY Dealer_Number 
            ORDER BY 
                CASE WHEN Date_To IS NULL THEN 1 ELSE 0 END DESC, -- 优先保留无结束日期的
                Date_From ASC, 
                ISNULL(Date_To, '9999-12-31') DESC -- 其次保留范围更大的
        ) AS keep_rank
    FROM (
        -- 先找出所有重叠的记录
        SELECT DISTINCT
            Dealer_Number,
            Date_From,
            Date_To
        FROM #tmp t1
        WHERE EXISTS (
            SELECT 1
            FROM #tmp t2
            WHERE t1.Dealer_Number = t2.Dealer_Number
                AND (t1.Date_From <> t2.Date_From OR t1.Date_To <> t2.Date_To OR (t1.Date_To IS NULL AND t2.Date_To IS NULL))
                AND t1.Date_From <= ISNULL(t2.Date_To, '9999-12-31')
                AND t2.Date_From <= ISNULL(t1.Date_To, '9999-12-31')
        )
    ) AS overlaps
)
-- 删除rank>1的重叠记录
DELETE FROM #tmp
WHERE (Dealer_Number, Date_From, Date_To) IN (
    SELECT Dealer_Number, Date_From, Date_To
    FROM overlapping_records
    WHERE keep_rank > 1
);

代码说明:

  • 先通过EXISTS找出所有重叠的记录,然后给这些记录按业务规则排序,标记出要保留的记录(keep_rank=1),最后删除其余的重叠记录。你可以根据实际需求调整ORDER BY后的排序规则。

为什么你之前的SQL无法生效?

你之前用GROUP BY和COUNT(Date_From) >1的方式,只能找出完全重复的记录(即Date_From和Date_To完全相同的行),但无法识别那些日期范围部分重叠、互相包含的情况,所以才得不到预期结果。

内容的提问来源于stack exchange,提问作者PeteMiller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:03:51