数据库原理与技术 - 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);
相关推荐
爱学习的阿磊几秒前
使用Fabric自动化你的部署流程
jvm·数据库·python
枷锁—sha6 分钟前
【SRC】SQL注入快速判定与应对策略(一)
网络·数据库·sql·安全·网络安全·系统安全
惜分飞19 分钟前
ORA-600 kcratr_nab_less_than_odr和ORA-600 4193故障处理--惜分飞
数据库·oracle
chian-ocean19 分钟前
CANN 生态进阶:利用 `profiling-tools` 优化模型性能
数据库·mysql
m0_5500246323 分钟前
持续集成/持续部署(CI/CD) for Python
jvm·数据库·python
AC赳赳老秦24 分钟前
代码生成超越 GPT-4:DeepSeek-V4 编程任务实战与 2026 开发者效率提升指南
数据库·数据仓库·人工智能·科技·rabbitmq·memcache·deepseek
啦啦啦_999937 分钟前
Redis-2-queryFormat()方法
数据库·redis·缓存
玄同7651 小时前
SQLite + LLM:大模型应用落地的轻量级数据存储方案
jvm·数据库·人工智能·python·语言模型·sqlite·知识图谱
吾日三省吾码1 小时前
别只会“加索引”了!这 3 个 PostgreSQL 反常识优化,能把性能和成本一起打下来
数据库·postgresql
chian-ocean1 小时前
百万级图文检索实战:`ops-transformer` + 向量数据库构建语义搜索引擎
数据库·搜索引擎·transformer