Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 甲骨文:如何;分组方式;超过一个范围?_Sql_Oracle_Plsql_Oracle10g - Fatal编程技术网

Sql 甲骨文:如何;分组方式;超过一个范围?

Sql 甲骨文:如何;分组方式;超过一个范围?,sql,oracle,plsql,oracle10g,Sql,Oracle,Plsql,Oracle10g,如果我有这样一张桌子: pkey age ---- --- 1 8 2 5 3 12 4 12 5 22 TO_CHAR(FLOOR(age/10) * 10) || '-' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1) SELECT TO_CHAR(FLOOR(age/10) * 10) || ' - ' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1)

如果我有这样一张桌子:

pkey   age
----   ---
   1     8
   2     5
   3    12
   4    12
   5    22
TO_CHAR(FLOOR(age/10) * 10) || '-' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1)
SELECT 
TO_CHAR(FLOOR(age/10) * 10) || ' - ' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1),
COUNT(*)
FROM tbl
GROUP BY FLOOR(age/10)
我可以“分组”来计算每个年龄

select age,count(*) n from tbl group by age;
age  n
---  -
  5  1
  8  1
 12  2
 22  1
我可以使用什么查询按年龄范围分组

  age  n
-----  -
 1-10  2
11-20  2
20+    1
我使用10gR2,但我也对任何11g特定的方法感兴趣

试试看:

select to_char(floor(age/10) * 10) || '-' 
|| to_char(ceil(age/10) * 10 - 1)) as age, 
count(*) as n from tbl group by floor(age/10);

将“年龄范围”表和“年龄范围”id字段添加到表中,并按该字段分组

//请原谅DDL,但你应该明白这一点

create table age_range(
age_range_id tinyint unsigned not null primary key,
name varchar(255) not null);

insert into age_range values 
(1, '18-24'),(2, '25-34'),(3, '35-44'),(4, '45-54'),(5, '55-64');
select
 count(*) as counter, p.age_range_id, ar.name
from
  person p
inner join age_range ar on p.age_range_id = ar.age_range_id
group by
  p.age_range_id, ar.name order by counter desc;
//再次原谅DML,但你应该明白

create table age_range(
age_range_id tinyint unsigned not null primary key,
name varchar(255) not null);

insert into age_range values 
(1, '18-24'),(2, '25-34'),(3, '35-44'),(4, '45-54'),(5, '55-64');
select
 count(*) as counter, p.age_range_id, ar.name
from
  person p
inner join age_range ar on p.age_range_id = ar.age_range_id
group by
  p.age_range_id, ar.name order by counter desc;
如果您愿意,您可以改进这个想法-在年龄范围表中添加从年龄到年龄的列,等等-但我将把这个留给您


希望这有帮助:)

这里有一个解决方案,它在子查询中创建一个“范围”表,然后使用该表从主表中分割数据:

SELECT DISTINCT descr
  , COUNT(*) OVER (PARTITION BY descr) n
FROM age_table INNER JOIN (
  select '1-10' descr, 1 rng_start, 10 rng_stop from dual
  union (
  select '11-20', 11, 20 from dual
  ) union (
  select '20+', 21, null from dual
)) ON age BETWEEN nvl(rng_start, age) AND nvl(rng_stop, age)
ORDER BY descr;
选择案例
如果使用Oracle 9i+,年龄时,您可能可以使用:

需要注意的是,您只能指定分区的数量,而不能指定断点本身。因此,您需要指定一个合适的数字。IE:对于100行,
NTILE(4)
将为四个存储桶/分区中的每一个分配25行。您不能嵌套分析函数,因此您必须使用子查询/子查询分解对它们进行分层,以获得所需的粒度。否则,请使用:

  SELECT CASE t.age
           WHEN BETWEEN 1 AND 10 THEN '1-10' 
           WHEN BETWEEN 11 AND 20 THEN '11-20' 
           ELSE '21+' 
         END AS age, 
         COUNT(*) AS n
    FROM TABLE t
GROUP BY CASE t.age
           WHEN BETWEEN 1 AND 10 THEN '1-10' 
           WHEN BETWEEN 11 AND 20 THEN '11-20' 
           ELSE '21+' 
         END

我必须根据一小时内出现的事务数对数据进行分组。我是通过从时间戳中提取小时来实现的:

select extract(hour from transaction_time) as hour
      ,count(*)
from   table
where  transaction_date='01-jan-2000'
group by
       extract(hour from transaction_time)
order by
       extract(hour from transaction_time) asc
;
给出输出:

HOUR COUNT(*)
---- --------
   1     9199 
   2     9167 
   3     9997 
   4     7218

正如您所见,这提供了一种很好的方法来对每小时的记录数进行分组。

我必须按天计算样本数。受@Clarkey的启发,我曾使用_CHAR将样本日期从时间戳提取为ISO-8601日期格式,并在GROUP by和ORDER by子句中使用该格式。(受启发,我还将其发布在这里,以防对其他人有用。)


你要找的,基本上是一个项目的数据

在x轴上显示年龄(或年龄范围),在y轴上显示计数n(或频率)

以最简单的形式,可以简单地计算每个不同年龄值的数量,如您所述:

SELECT age, count(*)
FROM tbl
GROUP BY age
但是,当x轴有太多不同的值时,可能需要创建组(或簇或桶)。在您的情况下,您按10的恒定范围分组

当。。。然后
每个范围的行-如果不是关于年龄的话,可能有数百行。相反,出于@NitinMidha提到的原因,@MatthewFlaschen的方法更可取

现在让我们构建SQL

首先,我们需要将年龄分为10个组,如下所示:

  • 0-9
  • 10-19
  • 20-29
  • 等等
这可以通过将年龄列除以10,然后计算结果的下限来实现:

FLOOR(age/10)
“FLOOR返回的最大整数等于或小于n”

然后我们采用原始SQL并用该表达式替换age

SELECT FLOOR(age/10), count(*)
FROM tbl
GROUP BY FLOOR(age/10)
这没问题,但我们还看不到范围。相反,我们只看到计算出的楼层值,
0,1,2。。。n

为了得到实际的下限,我们需要将它再次乘以10,这样我们得到
0,10,20。。。n

FLOOR(age/10) * 10
我们还需要每个范围的上限,即下限+10-1或

最后,我们将两者连接成如下字符串:

pkey   age
----   ---
   1     8
   2     5
   3    12
   4    12
   5    22
TO_CHAR(FLOOR(age/10) * 10) || '-' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1)
SELECT 
TO_CHAR(FLOOR(age/10) * 10) || ' - ' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1),
COUNT(*)
FROM tbl
GROUP BY FLOOR(age/10)
这将创建
“0-9”、“10-19”、“20-29”等

现在,我们的SQL如下所示:

pkey   age
----   ---
   1     8
   2     5
   3    12
   4    12
   5    22
TO_CHAR(FLOOR(age/10) * 10) || '-' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1)
SELECT 
TO_CHAR(FLOOR(age/10) * 10) || ' - ' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1),
COUNT(*)
FROM tbl
GROUP BY FLOOR(age/10)
最后,应用顺序和漂亮的列别名:

SELECT 
TO_CHAR(FLOOR(age/10) * 10) || ' - ' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1) AS range,
COUNT(*) AS frequency
FROM tbl
GROUP BY FLOOR(age/10)
ORDER BY FLOOR(age/10)
然而,在更复杂的场景中,这些范围可能不会被分组为大小为10的常量块,而是需要动态聚类。 Oracle提供了更高级的直方图功能,请参见

归功于@MatthewFlaschen的方法;我只解释了细节。

我的方法:

select range, count(1) from (
select case 
  when age < 5 then '0-4' 
  when age < 10 then '5-9' 
  when age < 15 then '10-14' 
  when age < 20 then '15-20' 
  when age < 30 then '21-30' 
  when age < 40 then '31-40' 
  when age < 50 then '41-50' 
  else                '51+' 
end 
as range from
(select round(extract(day from feedback_update_time - feedback_time), 1) as age
from txn_history
) ) group by range  
从中选择范围、计数(1)(
选择案例
当年龄小于5岁时,则为“0-4”
当年龄<10岁时,则为“5-9”
当年龄<15岁时,则为“10-14”
当年龄<20岁时,则为“15-20”
当年龄<30岁时,则为“21-30”
当年龄<40岁时,则为“31-40”
当年龄<50岁时,则为“41-50”
其他'51+'
结束
从
(选择轮次(提取(反馈更新时间中的日期-反馈时间),1)作为年龄
来自txn_历史
))按范围分组
  • 我可以灵活地定义范围
  • 我不重复select和group子句中的范围
  • 但是有人请告诉我,如何按大小排序
您可以尝试以下解决方案吗:

SELECT count (1), '1-10'  where age between 1 and 10
union all 
SELECT count (1), '11-20'  where age between 11 and 20
union all
select count (1), '21+' where age >20
from age 

这应该是这个问题的第一个也是唯一的答案。但是可能需要更多的格式。不,CASE语句使用short-circut求值short-circut求值如何在此查询中导致问题?因为案例是有序的,并且使用了Adrian your correct,所以这是对之前删除的评论的回复。有没有办法包含一个没有行的范围。示例:如果没有高于20的人,查询将返回一行(20+,0)?楼层/分区的巧妙用法!当我们有一个已定义的模式并且可以通过表达式计算组时,这种方法会更好。它不需要在查询中明确提及组,因此可以在不修改查询的情况下提供新组……这不起作用,它会导致错误ORA-00979:不是按表达式分组,因为在按表达式分组中缺少
ceil(age/10)
。但正如@NitinMidha所写,这种方法的方向更好,所以我投票支持这个答案。从其他回答来看,性能和灵活性不是重要的标准。列出的所有动态查询的解释计划都是可怕的,如果您的年龄范围发生变化,您必须修改代码。我猜,每一个都有自己的功能:P1完全扫描总是比2完全扫描快。此外,要求提供年龄范围统计数据的人可能在过去20多年中拥有相同的范围,并且没有改变这一点的意图。我非常确定物理列的性能将超过派生/计算列。事实上,它可能是一个理想的候选人