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

基于PERSON_ID与SERVICE_RANK分组合并连续日期区间的SQL需求

Solution for Consecutive SERVICE_RANK Grouping (Gaps and Islands Problem)

Got it, this is a classic gaps and islands scenario in SQL—exactly what you need to group consecutive rows with the same SERVICE_RANK while keeping non-consecutive same-rank segments separate. Let's break down how to solve this with window functions (no cursors required!):

Step-by-Step Explanation

The core idea is to use two ROW_NUMBER() calculations to create a grouping key that stays consistent for consecutive rows of the same SERVICE_RANK, and changes when the rank switches. Here's how it works:

  • First, assign a row number partitioned by PERSON_ID and SERVICE_RANK, ordered by START_DATE
  • Second, assign a row number partitioned only by PERSON_ID, ordered by START_DATE
  • The difference between these two row numbers will be identical for all consecutive rows in the same "island" of SERVICE_RANK

Full SQL Code (Tested with Your Sample Data)

DECLARE @TestDATA TABLE (PERSON_ID int, START_DATE date, END_DATE date,SERVICE_RANK int)
INSERT INTO @TestDATA VALUES
(123, '2018-01-31', '2018-02-14', 7),
(123, '2018-03-28', '2018-04-11', 4),
(123, '2018-04-12', '2018-04-30', 4),
(123, '2018-05-25', '2018-06-08', 7),
(123, '2018-06-08', '2018-06-15', 7),
(123, '2018-06-19', '2018-06-26', 7),
(123, '2018-06-26', '2018-09-28', 4),
(123, '2018-10-10', '2018-11-07', 7),
(123, '2018-11-27', '2018-12-11', 7),
(123, '2018-12-11', '2018-12-24', 7);

WITH RankedData AS (
    SELECT 
        PERSON_ID,
        START_DATE,
        END_DATE,
        SERVICE_RANK,
        -- Row number within same person + rank
        ROW_NUMBER() OVER (PARTITION BY PERSON_ID, SERVICE_RANK ORDER BY START_DATE) AS RankWithinGroup,
        -- Row number within just the person
        ROW_NUMBER() OVER (PARTITION BY PERSON_ID ORDER BY START_DATE) AS OverallRowNum
    FROM @TestDATA
),
GroupedData AS (
    SELECT 
        PERSON_ID,
        START_DATE,
        END_DATE,
        SERVICE_RANK,
        -- The difference creates a unique key for each consecutive rank group
        OverallRowNum - RankWithinGroup AS GroupKey
    FROM RankedData
)
SELECT 
    PERSON_ID,
    START_DATE,
    END_DATE,
    SERVICE_RANK,
    -- Assign a dense rank to each group key per person to get SERVICE_PERIOD
    DENSE_RANK() OVER (PARTITION BY PERSON_ID ORDER BY GroupKey) AS SERVICE_PERIOD
FROM GroupedData
ORDER BY PERSON_ID, START_DATE;

Expected Output

This query will produce exactly the result you're looking for:

PERSON_IDSTART_DATEEND_DATESERVICE_RANKSERVICE_PERIOD
1232018-01-312018-02-1471
1232018-03-282018-04-1142
1232018-04-122018-04-3042
1232018-05-252018-06-0873
1232018-06-082018-06-1573
1232018-06-192018-06-2673
1232018-06-262018-09-2844
1232018-10-102018-11-0775
1232018-11-272018-12-1175
1232018-12-112018-12-2475

Why This Works

  • The GroupKey value stays the same as long as the SERVICE_RANK is consecutive for a person. When the rank changes, the RankWithinGroup resets, making the difference (OverallRowNum - RankWithinGroup) jump to a new value.
  • DENSE_RANK() then converts these unique GroupKey values into sequential SERVICE_PERIOD numbers per person, which handles all edge cases (like repeated ranks with gaps, single-rank rows, or contiguous date overlaps).

This approach is efficient even for large datasets (tens of thousands of rows) since it uses set-based operations instead of cursors, which are much slower for bulk data.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:49:02