Search code examples
sqlt-sqlintervals

How do I merge intervals in TSQL?


I have a table that contains the following intervals of big integers:

start_point end_point
4999999995 4999999995
5000000000 5000000009
5000000000 5099999999
5000000010 5000000010
5000000011 5000000025
5000000026 5000000045
5090090903 5090090903
6000000000 6000000000

What I need is a TSQL query that merges these intervals, so they would be disjoint without any overlaps. In this case the result I am looking for is

start_point end_point
4999999995 4999999995
5000000000 5099999999
6000000000 6000000000

Solution

  • Thank you for everyone's effort on trying to answer my question. In the end I have managed to find a solution that uses a cursor. I'm aware of the fact that cursors are slow, but it still turned out to be faster than using nested selects or recursive CTEs. Below is my solution

        DECLARE @start_point BIGINT, @end_point BIGINT;
    
        DECLARE @merged_intervals TABLE (start_point BIGINT, end_point BIGINT);
    
        DECLARE interval_cursor CURSOR FAST_FORWARD
        FOR
        SELECT start_point, end_point
        FROM myTable
        ORDER BY start_point;
    
        OPEN interval_cursor;
        FETCH NEXT FROM interval_cursor INTO @start_point, @end_point;
    
        WHILE @@FETCH_STATUS = 0
        BEGIN
            DECLARE @merged_start_point BIGINT, @merged_end_point BIGINT;
            SET @merged_start_point = @start_point;
            SET @merged_end_point = @end_point;
            FETCH NEXT FROM interval_cursor INTO @start_point, @end_point;
            WHILE @@FETCH_STATUS = 0 AND @start_point <= @merged_end_point
            BEGIN
                IF @end_point > @merged_end_point
                    SET @merged_end_point = @end_point;
                FETCH NEXT FROM interval_cursor INTO @start_point, @end_point;
            END;
            INSERT INTO @merged_intervals (start_point, end_point)
            VALUES (@merged_start_point, @merged_end_point);
        END;
    
        CLOSE interval_cursor;
        DEALLOCATE interval_cursor;