SQL Challenge/Puzzle: How to merge nested ranges?

后端 未结 3 1770
醉酒成梦
醉酒成梦 2021-01-06 03:40
  • This challenge is based on a real life use-case involving IP ranges.
  • The solution I came with is based on the stack trace challenge I\'ve presented previously
3条回答
  •  一个人的身影
    2021-01-06 04:41

    Oracle solution 2

     WITH borders AS /*get all borders of interval*/ 
      (SELECT DISTINCT DECODE(is_end, 0, range_start, range_end) AS border 
                      ,is_end 
       FROM   ranges r, 
              (SELECT 0 AS is_end FROM dual UNION ALL 
               SELECT 1 AS is_end FROM dual)), 
     interv AS  /*get all intervals*/ 
      (SELECT border + is_end AS beg_int 
             ,lead(border) over(ORDER BY border, is_end ) 
               - lead(DECODE(is_end, 0, 1, 0)) over(ORDER BY border, is_end) AS end_int 
       FROM   borders 
       ORDER  BY 1) 
     SELECT i.beg_int 
           ,i.end_int 
           ,(SELECT MAX(r.range_val) keep (dense_rank FIRST ORDER BY r.range_end - r.range_start) 
           FROM ranges r 
           WHERE i.beg_int >= r.range_start AND i.end_int <= r.range_end) AS range_val   
     FROM   interv i 
     WHERE  beg_int <= end_int OR end_int IS NULL 
     ORDER  BY i.beg_int; 
    

    Add solution without self join : EDIT: fixed defect.

     WITH intervals AS 
      (SELECT DECODE(is_end, -1, range_val, NULL) AS range_val 
             ,DECODE(is_end, -1, range_start, range_end) AS border 
             ,is_end 
             ,- (SUM(is_end) over(ORDER BY DECODE(is_end, -1, range_start, range_end), is_end, (range_end - range_start) * is_end)) AS poss 
             ,(range_end - range_start) * is_end AS ord2 
       FROM   ranges r 
             ,(SELECT -1 AS is_end FROM   dual UNION ALL 
               SELECT 1  AS is_end FROM   dual)), 
     range_stack AS 
      (SELECT border + DECODE(is_end, 1, 1, 0) AS begin_int 
             ,lead(border) over(ORDER BY border, is_end, ord2) 
               + DECODE(lead(is_end) over(ORDER BY border, is_end, ord2), 1, 0, -1) AS end_int 
             ,last_value(range_val ignore NULLS) over(PARTITION BY poss ORDER BY border, is_end, ord2) AS range_val 
       FROM   intervals) 
     SELECT begin_int 
           ,end_int 
           ,range_val 
     FROM   range_stack 
     WHERE  end_int >= begin_int 
            OR end_int IS NULL;
    

提交回复
热议问题