Oracle 数据库基础入门(四):分组与联表查询的深度探索(上)
在 Oracle 数据库的学习进程中,分组查询与联表查询是进阶阶段的重要知识点,它们如同数据库操作的魔法棒,能够从复杂的数据中挖掘出有价值的信息。对于 Java 全栈开发者而言,掌握这些技能不仅有助于高效地处理数据库数据,更是构建强大后端应用的关键。接下来,让我们深入探究这两个重要的数据库操作技巧。
目录
一、分组查询
(一)分组查询基础
(二)复合分组
(三)having 过滤
一、分组查询
(一)分组查询基础
分组查询允许我们根据指定的字段将数据进行分组,然后对每个组进行聚合统计操作。其语法结构为:
Select [字段列表] from 表的表名 [where 条件筛选] [order by 排序字段 asc/desc] [ group by 分组字段];
数据库的核心价值之一便是对数据进行聚合统计,以方便管理和分析。为了更好地理解分组查询,我们先创建一个goods_info
表,并插入一些示例数据。
create table goods_info (
id number primary key, -- 商品id,主键
good_name nvarchar2(100) not null, -- 商品名称,不允许为空
good_price number(10, 2) not null, -- 商品价格,不允许为空
good_store number, -- 库存数量
good_sales number, -- 销量
good_type nvarchar2(50), -- 商品类型
make_address nvarchar2(200) -- 生产地地址
);
-- 插入第1条数据
INSERT INTO goods_info (id, good_name, good_price, good_store, good_sales, good_type, make_address)
VALUES (1, '苹果iPhone 13', 6999.00, 100, 500, '手机', '中国深圳');
-- 插入第2条数据
INSERT INTO goods_info (id, good_name, good_price, good_store, good_sales, good_type, make_address)
VALUES (2, '华为Mate 40 Pro', 7999.00, 50, 300, '手机', '中国深圳');
-- 插入第3条数据
INSERT INTO goods_info (id, good_name, good_price, good_store, good_sales, good_type, make_address)
VALUES (3, '小米11 Ultra', 3999.00, 75, 450, '手机', '中国东莞');
-- 插入第4条数据
INSERT INTO goods_info (id, good_name, good_price, good_store, good_sales, good_type, make_address)
VALUES (4, '美的电饭煲', 1299.00, 200, 1500, '家用电器', '中国北京');
-- 插入第5条数据
INSERT INTO goods_info (id, good_name, good_price, good_store, good_sales, good_type, make_address)
VALUES (5, '海尔洗衣机', 1599.00, 150, 1200, '家用电器', '中国北京');
-- 插入第6条数据
INSERT INTO goods_info (id, good_name, good_price, good_store, good_sales, good_type, make_address)
VALUES (6, '索尼Xperia 1 III', 599.00, 100, 800, '手机', '中国东莞');
-- 插入第7条数据
INSERT INTO goods_info (id, good_name, good_price, good_store, good_sales, good_type, make_address)
VALUES (7, '戴森吸尘器', 2999.00, 60, 350, '家用电器', '中国北京');
-- 插入第8条数据
INSERT INTO goods_info (id, good_name, good_price, good_store, good_sales, good_type, make_address)
VALUES (8, '飞利浦空气净化器', 299.00, 500, 2000, '家用电器', '中国北京');
特别要注意的是,分组之后,select
后面可以跟分组字段以及聚合函数。例如:
- 按照商品类型,分别统计不同的商品类型有多少种对应的产品:
select good_type,count(*) from goods_info group by good_type;
- 按照商品类型,分别统计不同的商品类型的最高价:
select good_type,max(good_price) from goods_info group by good_type;
- 按照商品类型,分别统计不同的商品类型的最低价、平均价、总价:
select
good_type,
max(good_price) as maxPrice,
min(good_price) as minPrice,
avg(good_price) as avgPrice,
sum(good_price) as sumPrice
from
goods_info
group by
good_type;
在 Java 全栈开发中,当我们开发一个电商数据分析模块时,可能会利用这些分组查询来统计不同商品类型的销售情况,然后通过 Java 代码将统计结果展示在前端页面上,为运营人员提供数据支持。
(二)复合分组
复合分组是在分组的基础上,进一步对数据进行分组。这就好比对书籍先按照类别分组,然后在每个类别中再按照作者分组。例如,按照商品类型和生产地,统计不同的商品类型在不同生产地分别有多少种对应的商品:
select
good_type,
make_address,
count(*)
from
goods_info
group by
good_type,
make_address;
通过复合分组,我们可以获取更细致的数据统计信息。以下是一些练习题:
- 按照学生姓名分组,统计不同的学生总分是多少,并按照总分进行降序排列:
select
name,
sum( score ) as total
from
student_exam_info
group by
name
order by
total desc;
- 按照考试科目分组统计,不同科目最高分、最低分、平均分是多少:
select subject,max(score),min(score),avg(score) from student_exam_info group by subject;
- 查询语文科目,不同的班级各自的平均分是多少:
select class_name,avg(score) from student_exam_info where subject = '语文' group by class_name;
(三)having 过滤
having
关键字用于对聚合后的结果进行再次过滤。其语法为:
select [字段列表] from 表的表名 [where 条件筛选] [order by 排序字段 asc/desc] [group by 分组字段] [having 聚合过滤条件];
例如,按照学生姓名分组,统计不同的学生总分,只想看总分大于等于 260 以上的学生:
select
name,
sum( score ) as total
from
student_exam_info
group by
name
having sum( score ) >= 260
order by total desc;
又如,查询语文科目,不同的班级各自的平均分,并查询平均分高于 90 以上的班级:
select class_name,avg(score) from student_exam_info where subject = '语文' group by class_name having avg(score) >= 90;
这里需要注意where
和having
的区别:where
执行在分组聚合之前,用于对原始数据进行筛选;而having
执行在分组聚合之后,用于对聚合后的结果进行过滤。在 Java 全栈开发中,当我们从数据库获取分组统计数据时,需要根据业务需求合理使用where
和having
,确保获取到准确的数据。
(三)where 与 having 的区别总结
- 执行顺序:
where
执行在分组聚合之前,用于对原始数据进行过滤,以减少参与分组聚合的数据量;而having
执行在分组聚合之后,用于对聚合后的结果进行筛选。 - 适用对象:
where
子句不能使用聚合函数,因为此时聚合操作尚未进行;而having
子句主要用于对聚合函数的结果进行条件判断。
在实际的 Java 全栈开发项目中,正确理解和运用where
与having
的区别至关重要。例如,在一个电商订单数据分析系统中,如果要统计每个用户的订单总金额,并筛选出订单总金额大于 1000 元的用户,我们需要使用group by
对用户进行分组,使用sum
函数计算订单总金额,然后使用having
子句筛选出符合条件的用户。而如果要先筛选出特定时间段内的订单数据,再进行分组统计,那么对时间段的筛选就应该使用where
子句。
三、企业工作小技巧
- 合理使用索引:在进行分组查询时,如果分组字段上有索引,查询性能会得到显著提升。例如,在
goods_info
表中,如果经常按照good_type
进行分组查询,可以考虑在good_type
字段上创建索引。但要注意,索引并非越多越好,过多的索引会增加数据插入和更新的时间开销,因为数据库在插入或更新数据时,不仅要更新表数据,还要更新相关的索引。 - 避免复杂分组:尽量避免在一个查询中使用过多的分组字段或复杂的分组逻辑,因为这可能会导致查询性能急剧下降。如果业务需求确实复杂,可以考虑将大查询拆分成多个小查询,逐步进行数据处理。例如,在一个涉及多个维度分组统计的报表生成场景中,如果直接进行多维度复合分组查询,可能会因为数据量过大而导致查询超时。此时,可以先按照主要维度进行分组统计,将结果存储在临时表中,然后再对临时表进行二次分组统计,以降低查询复杂度。
- 结合 Java 代码优化:在 Java 全栈开发中,不要仅仅依赖数据库进行所有的数据处理。可以在数据库层面进行必要的分组和聚合操作,然后将结果返回给 Java 代码进行进一步的处理和筛选。例如,在统计学生成绩时,数据库查询返回每个学生的总分和平均分,Java 代码可以根据业务规则对这些数据进行进一步的筛选和分析,如计算每个学生的成绩排名等,这样可以减轻数据库的负担,提高系统的整体性能。
通过对 Oracle 数据库分组查询的深入学习和实践,我们掌握了一种强大的数据处理工具。在未来的 Java 全栈开发工作中,灵活运用分组查询能够帮助我们高效地解决各种数据统计和分析问题,为企业提供有价值的数据支持。希望大家不断练习,将这些知识转化为实际的开发能力。
因为篇幅原因后续的联合查询&子查询将在下一篇补充完整
Oracle 数据库基础入门(四):分组与联表查询的深度探索(下)-CSDN博客