SQL - 范围是否在连续范围内

时间:2014-02-10 19:17:14

标签: sql oracle range

我真的很难过......

假设我有一个“有效数字”表,其中列BEGIN_NUM和END_NUM表示一系列有效数字。该表看起来像这样:

ID BEGIN_NUM END_NUM
-- --------- -------
 A       1      10         
 B      11      20
 C      21      30
 D      55      70

我们给出了一个范围的开始编号和结束编号。我需要开发一个SQL查询来查看该数字范围是否都是有效数字。

简单的情况:给定2和8作为我们的开始和结束我们的范围,这将通过,因为它落在行A的范围内。

硬案例:(#1)给定5和15,这将通过,因为它属于行A和行B,这基本上是彼此的延续,形成跨越两行的一个大范围。 (1-10& 11-20 = 1-20)。

给出5和25的

(#2),除了它跨越几行而不是两行之外,它也会出于与(#1)相同的原因。 (1-10& 11-20& 21-30 = 1-30)

给出27和57的

(#3),这将失败,因为即使开始和结束数字落在一个范围内,行C和行D之间也存在间隙(所以这将使我们给出的数字为31-54)范围无效)。

我真的不需要使用此查询返回任何数据,只显示该范围包含所有有效数字。如果重要,请使用Oracle。

这是我到目前为止所做的:

select count(*) 
from (select start_num, end_num
    from ae_valid_vendor_nums
    where '5' BETWEEN start_num AND end_num) tbl1,
 (select start_num, end_num
    from ae_valid_vendor_nums
    where '25'  BETWEEN start_num AND end_num) tbl2
where (tbl1.end_num - tbl2.start_num = -1) 
OR (tbl1.start_num = tbl2.start_num AND tbl1.end_num = tbl2.end_num)

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

有趣的小问题。我们的想法是计算每组内的重叠,将重叠相加,然后与实际范围进行比较。以下查询通过一些示例执行此操作。它输出的内容比您要求的多一点,但这应该有助于您了解它的工作原理:

with ValidNumbers as (
     select 1 as begin_num, 10 as end_num from dual union all
     select 11, 20 from dual union all
     select 21, 30 from dual union all
     select 55, 70 from dual
    )
select v_begin, v_end,
       sum(1 + (case when v_end >= vn.end_num then vn.end_num else v_end end) -
           (case when v_begin >= vn.begin_num then v_begin else vn.begin_num end)
          ) as SumInRecords,
       max(1 + v_end - v_begin) as TheRange,
       (case when sum(1 + (case when v_end >= vn.end_num then vn.end_num else v_end end) -
                      (case when v_begin >= vn.begin_num then v_begin else vn.begin_num end)
                     ) =
                   max(1 + v_end - v_begin)
             then 'All' else 'Missing'
         end)
from ValidNumbers vn cross join
     (select 2 as v_begin, 8 as v_end from dual union all
      select 2, 18 from dual union all
      select 2, 28 from dual union all
      select 2, 38 from dual
     ) const
where v_begin <= vn.end_num and
      v_end >= vn.begin_num
group by v_begin, v_end;

答案 1 :(得分:1)

一种方法是使用计数表,然后将计数计数与两个终点的差异进行比较(加一,因为它包括在内)

这是例如

WITH validnumbers 
     AS (SELECT 1  AS begin_num, 10 AS end_num FROM   dual 
         UNION ALL SELECT 11, 20          FROM   dual 
         UNION ALL SELECT 21, 30          FROM   dual 
         UNION ALL SELECT 55, 70          FROM   dual), 
     tally 
     AS (SELECT LEVEL num 
         FROM   dual 
         CONNECT BY LEVEL <= 100 
         ORDER  BY LEVEL), 
     test 
     AS (SELECT t.num 
         FROM   validnumbers v 
                inner join tally t 
                        ON v.begin_num <= t.num 
                           AND v.end_num >= t.num 
         WHERE  t.num >= 27 
                AND t.num <= 57) 
SELECT Count(num), 
       (57 - 27) + 1
FROM   test

生成此结果的正确失败案例

COUNT(NUM)  (57-27)+1
---------- ----------
         7         31 

Demo

用25和5代替57和27给我们一个正确的传球

COUNT(NUM)   (25-5)+1
---------- ----------
        21         21 

Demo