数据库原理与技术 - 3-5 SQL查询训练(习题)

包含 "Sailors(水手)""Reserves(预订记录)" 和 "Boats(船只)" 三个关系表,数据如下:

预订记录表(R1)

sid bid day
22 101 10/10/96
58 103 11/12/96

船只表(B1)

bid bname color
101 tiger red
103 lion green
105 hero blue

水手表(S1)

sid sname rating - age
22 dustin 7 45.0
31 lubber 8 55.5
58 rusty 10 35.0

水手表(S2)

sid sname rating age
28 yuppy 9 35.0
31 lubber 8 55.5
44 guppy 5 35.0
58 rusty 10 35.0

下面是查询案例
查询 1:查询预订了 103 号船只的水手姓名

三种方法:

sql 复制代码
SELECT S.sname
FROM Sailors S, Reserves R
WHERE S.sid = R.sid AND R.bid = 103;

SELECT S.sname
FROM Sailors S
WHERE S.sid IN (SELECT R.sid
                FROM Reserves R
                WHERE R.bid = 103);
                
SELECT S.sname
FROM Sailors S
WHERE EXISTS (SELECT *
              FROM Reserves R
              WHERE R.bid = 103 AND S.sid = R.sid);

查询 2:查询至少预订过一艘船的水手

sql 复制代码
SELECT S.sid
FROM Sailors S, Reserves R
WHERE S.sid = R.sid;

相关问题:

1.给该查询添加DISTINCT关键字会有区别吗?

对 sid 去重,只返回 "有预约记录的唯一水手 ID"。

2.若将SELECT子句中的S.sid替换为S.sname,效果如何?

结果列从 "水手 ID" 变成 "水手姓名",但重复逻辑和 sid 一致

3.对替换后的查询添加DISTINCT关键字会有区别吗?

有区别,且区别分两种场景:

场景 1:姓名无重复(如示例)

无 DISTINCT:姓名重复(和预约次数一致);

加 DISTINCT:姓名去重,只返回 "有预约记录的唯一水手姓名"。

场景 2:姓名有重复(比如 sid=1 和 4 都叫张三,且都有预约)

无 DISTINCT:姓名 "张三" 会出现(sid=1 的预约次数 + sid=4 的预约次数)次;

加 DISTINCT:姓名 "张三" 只出现 1 次(不管多少个同名水手、多少次预约)。

优化查询

sql 复制代码
SELECT DISTINCT S.sid, S.sname
FROM Sailors S, Reserves R
WHERE S.sid = R.sid;

DISTINCT作用于 "所有选中的列"(sid+sname),而非单列 ------ 只有当 sid 和 sname同时重复时,才会去重,既保留 ID + 姓名的完整信息,又避免重复,是更实用的写法。
查询 3:查询预订过红色或绿色船只的水手 ID

sql 复制代码
SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid 
AND (B.color='red' OR B.color='green');
--FROM三张表先做全量笛卡尔积,再通过WHERE过滤有效关联和颜色
--条件,效率略低,结果不会自动去重!如果某水手同时预订了红色和绿
--色船只,其 ID 会重复出现
(SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid AND B.color='red')
UNION
( SELECT S.sid FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid AND B.color='green';
--UNION关键字会自动剔除重复的水手 ID(即使某水手同
--时订了红、绿船,ID 只显示一次);

查询 4:查询预订过红色和绿色船只的水手 ID

sql 复制代码
--方法 1(多表连接):
SELECT S.sid
FROM Sailors S, Boats B1, Reserves R1, Boats B2, Reserves R2
WHERE S.sid = R1.sid AND R1.bid = B1.bid
  AND S.sid = R2.sid AND R2.bid = B2.bid
  AND (B1.color = 'red' AND B2.color = 'green');

--方法 2(使用INTERSECT交集查询)
(SELECT S.sid
 FROM Sailors S, Boats B, Reserves R
 WHERE S.sid = R.sid AND R.bid = B.bid AND B.color = 'red')
INTERSECT
(SELECT S.sid
 FROM Sailors S, Boats B, Reserves R
 WHERE S.sid = R.sid AND R.bid = B.bid AND B.color = 'green');

--方法 3(使用子查询IN):
SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid = R.sid AND R.bid = B.bid AND B.color = 'red'
  AND S.sid IN (SELECT S2.sid
                FROM Sailors S2, Boats B2, Reserves R2
                WHERE S2.sid = R2.sid AND R2.bid = B2.bid AND B2.color = 'green');

查询 5:查询预订了所有船只的水手姓名

sql 复制代码
--方法 1(使用EXCEPT差集查询):
SELECT S.sname FROM Sailors S
WHERE NOT EXISTS -- 条件:"不存在以下差集"则保留该水手
 (SELECT B.bid FROM Boats B) -- 集合A:所有船只的bid(全部船)
EXCEPT
(SELECT R.bid FROM Reserves R  WHERE R.sid = S.sid)); 
 -- 集合B:当前水手S预约过的船只bid
-- NOT EXISTS 表示:如果 "没预约过的船只 bid" 是
--空集(即差集无元素)→ 说明 S 预约了所有船 → 保留该水手;

--方法 2(双重NOT EXISTS):
--找不到任何一艘船 B,是水手 S 没预约过的 
-- → 等价于「水手 S 预约了所有船只」。
SELECT S.sname
FROM Sailors S
WHERE NOT EXISTS (SELECT B.bid
                  FROM Boats B
                  WHERE NOT EXISTS (SELECT R.bid
                                    FROM Reserves R
                                    WHERE R.bid = B.bid AND R.sid = S.sid));

-- NOT EXISTS 如果下面的 "内层查询" 找不到任何行,就保留当前水手 S

查询 6:查询年龄最大的水手姓名及年龄

sql 复制代码
SELECT S.sname, S.age
FROM Sailors S
WHERE S.age = (SELECT MAX(S2.age) FROM Sailors S2);

查询 7:查询每艘红色船只的预订次数

sql 复制代码
SELECT B.bid, COUNT(*) AS scount
FROM Boats B, Reserves R
WHERE R.bid = B.bid AND B.color = 'red'
GROUP BY B.bid;

-- 方法 2(使用HAVING子句筛选红色船只):
SELECT B.bid, COUNT(*) AS scount
FROM Boats AS B, Reserves AS R
WHERE R.bid = B.bid
GROUP BY B.bid
HAVING B.bid IN (SELECT bid
                 FROM Boats 
                 WHERE color = 'red');

查询 8:查询每个评级对应的最年轻水手姓名

sql 复制代码
步骤 1:查询每个评级的最小年龄
SELECT MIN(S.age)
FROM Sailors S
GROUP BY S.rating;
步骤 2:关联查询最年轻水手姓名
SELECT S1.sname
FROM Sailors AS S1,
     (SELECT S2.rating, MIN(S2.age) AS minage
      FROM Sailors AS S2
      GROUP BY S2.rating) AS R
WHERE S1.rating = R.rating AND S1.age = R.minage;

查询 9:查询满足特定条件的每个评级对应的最年轻水手年龄

场景 1:年龄≥18 岁,且每个评级至少有 2 名符合该年龄条件的水手

sql 复制代码
SELECT S.rating, MIN(S.age) AS minage
FROM Sailors S
WHERE S.age >= 18
GROUP BY S.rating
HAVING COUNT(*) > 1;

场景 2:年龄≥18 岁,且每个评级至少有 2 名水手(年龄不限)

sql 复制代码
SELECT S.rating, MIN(S.age) AS minage
FROM Sailors S
WHERE S.age >= 18
GROUP BY S.rating
HAVING 1 < (SELECT COUNT(*)
            FROM Sailors S2
            WHERE S2.rating = S.rating);

查询 10:查询平均年龄在所有评级中最小的评级

sql 复制代码
SELECT Temp.rating
FROM (SELECT S.rating, AVG(S.age) AS avgage
      FROM Sailors S
      GROUP BY S.rating) Temp
WHERE Temp.avgage = (SELECT MIN(Temp.avgage) FROM Temp);
相关推荐
while(1){yan}19 小时前
Spring事务
java·数据库·spring boot·后端·java-ee·mybatis
盛世宏博北京20 小时前
高效环境管控:楼宇机房以太网温湿度精准监测系统方案
开发语言·数据库·php·以太网温湿度变送器
运维行者_20 小时前
2026 技术升级,OpManager 新增 AI 网络拓扑与带宽预测功能
运维·网络·数据库·人工智能·安全·web安全·自动化
gfdhy20 小时前
【C++实战】多态版商品库存管理系统:从设计到实现,吃透面向对象核心
开发语言·数据库·c++·microsoft·毕业设计·毕设
Elastic 中国社区官方博客20 小时前
Elasticsearch:上下文工程 vs. 提示词工程
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
小唐同学爱学习20 小时前
如何解决海量数据存储
java·数据库·spring boot·mysql
wWYy.21 小时前
详解redis(15):缓存雪崩
数据库·redis·缓存
zzcufo21 小时前
多邻国第五阶段第13部分
java·开发语言·数据库
这周也會开心21 小时前
Redis相关知识点
数据库·redis·缓存
小白爱运维21 小时前
MySQL升级8.0.44后登录报错-系统表不支持'MyISAM'存储引擎
数据库·mysql