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
- 等等
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多年中拥有相同的范围,并且没有改变这一点的意图。我非常确定物理列的性能将超过派生/计算列。事实上,它可能是一个理想的候选人