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

Posted

技术标签:

【中文标题】在 oracle 中查询范围的更好方法【英文标题】:Better way to query ranges in oracle 【发布时间】:2021-10-29 01:50:36 【问题描述】:

我有以下问题

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 的值

【问题讨论】:

【参考方案1】:

您也可以使用它。它利用了 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

【讨论】:

【参考方案2】:

您可以使用带有窗口函数的基本查询结构:

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

【讨论】:

结合@justincave 最佳答案并使用order by min(value) DESC 我得到了想要的结果,谢谢【参考方案3】:

这个怎么样(定义 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');

【讨论】:

【参考方案4】:

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

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;

【讨论】:

【参考方案5】:

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

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

不过,假设您想要任意范围,我会创建一个 range 表或使用 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

【讨论】:

这不会给出相同的结果,因为 OP 的解决方案没有范围的上限。 @MT0 - 是的。通常有一个可以选择的合理上限。您还可以在连接中声明一个上限或下限null 和代码。您还可以避免重复一个范围的上限和另一个范围的下限(假设意图是具有连续范围),但这会增加查询的复杂性。 该列仅包含 1 位数的值,这使我可以使用上查询来简化选择,范围从 .5 上限到 -4.0 下限

以上是关于在 oracle 中查询范围的更好方法的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Oracle SQL 的 Where 语句中添加大小写

oracle中怎么查询一个分区表中某个分区的具体信息,例如这个分区的范围,这个可以查得到吗

DATEDIFF() 或 BETWEEN 用于 SQL 查询中的日期范围

redis范围查询应用 数据库 数据库学习 Redis redis范围查询的方法

oracle分区表的使用和查询

oracle日期时间范围查询