在 oracle 中查询范围的更好方法

Better way to query ranges in oracle

我有以下查询

SELECT  0.0, count(*) FROM tbl_a where value >=  0.0
UNION
SELECT  0.1, count(*) FROM tbl_a where value >=  0.1
UNION
SELECT  0.2, count(*) FROM tbl_a where value >=  0.2
UNION
SELECT  0.3, count(*) FROM tbl_a where value >=  0.3
UNION
SELECT  0.4, count(*) FROM tbl_a where value >=  0.4
UNION
SELECT  0.5, count(*) FROM tbl_a where value >=  0.5;

这很好用,我得到了我希望的结果,但是维护和扩展很痛苦

我试过像这样分组

SELECT  
    CASE 
        WHEN value >= 0.5 THEN 0.5
        WHEN value >= 0.4 THEN 0.4
        WHEN value >= 0.3 THEN 0.3
        WHEN value >= 0.2 THEN 0.2
        WHEN value >= 0.1 THEN 0.1
        WHEN value >= 0.0 THEN 0.0
        END as mag,
        count(*) as numberOfCases
FROM tbl_a
GROUP BY CASE 
        WHEN value >= 0.5 THEN 0.5
        WHEN value >= 0.4 THEN 0.4
        WHEN value >= 0.3 THEN 0.3
        WHEN value >= 0.2 THEN 0.2
        WHEN value >= 0.1 THEN 0.1
        WHEN value >= 0.0 THEN 0.0
END
ORDER BY MAG

但是结果并没有给我0值的组,也没有累积,

例如,当我声明 value >= 0 时,结果应包括所有高于或等于 0.0 的值,但它不包括仅包含 0.0

的值

你的范围是任意的吗?如果您只想将 value 舍入到最接近的十分之一,然后聚合

select round( value, 1 ),
       count(*)
  from table_a a
 group by round( value, 1 );

不过,假设您想要任意范围,我会创建一个 range table 或使用 CTE 模拟一个范围,具体取决于您是否要将同一组范围应用于不同的查询。

with ranges as (
  select 0.1 lower_bound, 0.2 upper_bound from dual
  union all
  select 0.2, 0.3 from dual
  union all
  select 0.3, 0.4 from dual
  union all
  select 0.4, 0.5 from dual
  union all
  select 0.5, 999 from dual
)
select r.lower_bound, count(*)
  from table_a a
       join ranges r
         on a.value >= r.lower_bound
        and a.value <  r.upper_bound
 group by r.lower_bound

您可以将边界放入子查询中:

WITH lower_bounds (lower_bound) AS (
  SELECT 0.0 FROM DUAL UNION ALL
  SELECT 0.1 FROM DUAL UNION ALL
  SELECT 0.2 FROM DUAL UNION ALL
  SELECT 0.3 FROM DUAL UNION ALL
  SELECT 0.4 FROM DUAL UNION ALL
  SELECT 0.5 FROM DUAL
)
SELECT lower_bound,
       COUNT(*)
FROM   tbl_a
       INNER JOIN lower_bounds
       ON (value >=  lower_bound)
GROUP BY lower_bound;

你也可以用集合来写:

SELECT b.COLUMN_VALUE AS lower_bound,
       COUNT(*)
FROM   tbl_a t
       INNER JOIN TABLE(SYS.ODCINUMBERLIST(0.0, 0.1, 0.2, 0.3, 0.4, 0.5)) b
       ON (t.value >=  b.COLUMN_VALUE)
GROUP BY b.COLUMN_VALUE;

这个怎么样(定义 10 个范围起点然后连接到它们,按范围起点分组):

with ranges (s) as
     ( select (rownum -1) / 10 from xmltable('0 to 5') )
select r.s as range_start
     , count(*)
from   tbl_a t
       join ranges r on t.value >= r.s
group by r.s
order by 1;

测试数据:

create table tbl_a (value) as
select dbms_random.value from xmltable('1 to 100');

您可以将基本查询结构与 window 函数结合使用:

SELECT (CASE WHEN value >= 0.5 THEN 0.5
             WHEN value >= 0.4 THEN 0.4
             WHEN value >= 0.3 THEN 0.3
             WHEN value >= 0.2 THEN 0.2
             WHEN value >= 0.1 THEN 0.1
             WHEN value >= 0.0 THEN 0.0
        END) as mag,
       COUNT(*) as numberOfCases,
       SUM(COUNT(*)) OVER (ORDER BY MIN(value)) as cumulative_numberOfCases
FROM tbl_a
GROUP BY (CASE WHEN value >= 0.5 THEN 0.5
               WHEN value >= 0.4 THEN 0.4
               WHEN value >= 0.3 THEN 0.3
               WHEN value >= 0.2 THEN 0.2
               WHEN value >= 0.1 THEN 0.1
               WHEN value >= 0.0 THEN 0.0
          END)
ORDER BY MAG

你也可以用这个。它利用 RANGE 窗口 条款。

with t (val) as (
select 0.0 from dual union all
select 0.0 from dual union all
select 0.1 from dual union all
select 0.1 from dual union all
select 0.1 from dual union all
select 0.2 from dual union all
select 0.2 from dual union all
select 0.2 from dual union all
select 0.2 from dual union all
select 0.3 from dual union all
select 0.3 from dual union all
select 0.3 from dual union all
select 0.4 from dual union all
select 0.4 from dual union all
select 0.4 from dual union all
select 0.4 from dual union all
select 0.4 from dual union all
select 0.5 from dual union all
select 0.5 from dual
)
select val, cnt 
from (
  select val
    , count(*)over(order by val range between current row and unbounded following) cnt
  from t
  )
group by val, cnt
order by 1
;

db<>fiddle