SQL必知必会题解
# SQL 必知必会题解
# 检索数据
# 从 Customers 表中检索所有的 ID
现有表 Customers 如下:
cust_id |
---|
A |
B |
C |
【问题】编写 SQL 语句,从 Customers 表中检索所有的 cust_id
答案:
select cust_id from Customers;
# 检索并列出已订购产品的清单⭐️
表 OrderItems 含有非空的列 prod_id 代表商品 id,包含了所有已订购的商品(有些已被订购多次)。
prod_id |
---|
a1 |
a2 |
a3 |
a4 |
a5 |
a6 |
a7 |
【问题】编写 SQL 语句,检索并列出所有已订购商品(prod_id)的去重后的清单。
答案:
select distinct prod_id from OrderItems;
# 检索所有列
现在有 Customers 表(表中含有列 cust_id 代表客户 id,cust_name 代表客户姓名)
cust_id | cust_name |
---|---|
a1 | andy |
a2 | ben |
a3 | tony |
a4 | tom |
a5 | an |
a6 | lee |
a7 | hex |
【问题】需要编写 SQL 语句,检索所有列。
答案:
select cust_id, cust_name from Customers;
# 排序检索数据
# 检索顾客名称并且排序
有表 Customers,cust_id 代表客户 id,cust_name 代表客户姓名。
cust_id | cust_name |
---|---|
a1 | andy |
a2 | ben |
a3 | tony |
a4 | tom |
a5 | an |
a6 | lee |
a7 | hex |
【问题】从 Customers 中检索所有的顾客名称(cust_name),并按从 Z 到 A 的顺序显示结果。(其实就是降序)
答案:
select cust_name from Customers order by cust_name desc;
# 对顾客 ID 和日期排序
有 Orders 表
cust_id | order_num | order_date |
---|---|---|
andy | aaaa | 2021-01-01 00:00:00 |
andy | bbbb | 2021-01-01 12:00:00 |
bob | cccc | 2021-01-10 12:00:00 |
dick | dddd | 2021-01-11 00:00:00 |
【问题】编写 SQL 语句,从 Orders 表中检索顾客 ID(cust_id)和订单号(order_num),并先按顾客 ID 对结果进行排序,再按订单日期倒序排列。
答案:
select cust_id, order_num from Orders order by cust_id, order_date desc;
# 按照数量和价格排序⭐️
假设有一个 OrderItems 表
quantity | item_price |
---|---|
1 | 100 |
10 | 1003 |
2 | 500 |
【问题】编写 SQL 语句,显示 OrderItems 表中的数量(quantity)和价格(item_price),并按数量由多到少、价格由高到低排序。
select quantity, item_price from OrderItems order by quantity desc,item_price desc;
# 检查 SQL 语句
有 Vendors 表
vend_name |
---|
海底捞 |
小龙坎 |
大龙燚 |
【问题】下面的 SQL 语句有问题吗?尝试将它改正确,使之能够正确运行,并且返回结果根据 vend_name 逆序排列
SELECT vend_name,
FROM Vendors
ORDER vend_name DESC;
2
3
答案:少了个 by
select vend_name from Vendors order by vend_name desc;
# 过滤数据
# 返回固定价格的产品
有表 Products
prod_id | prod_name | prod_price |
---|---|---|
a0018 | sockets | 9.49 |
a0019 | iphone13 | 600 |
b0018 | gucci t-shirts | 1000 |
【问题】从 Products 表中检索产品 ID(prod_id)和产品名称(prod_name),只返回价格为 9.49 美元的产品。
答案:
select prod_id, prod_name from Products where prod_price = 9.49;
# 返回更高价格的产品
Products 表
prod_id | prod_name | prod_price |
---|---|---|
a0018 | sockets | 9.49 |
a0019 | iphone13 | 600 |
b0019 | gucci t-shirts | 1000 |
【问题】编写 SQL 语句,从 Products 表中检索产品 ID(prod_id)和产品名称(prod_name),只返回价格为 9 美元或更高的产品。
答案:
select prod_id, prod_name from Products where prod_price >= 9;
# 返回产品并且按照价格排序⭐️
有 Products 表
prod_id | prod_name | prod_price |
---|---|---|
a0011 | egg | 3 |
a0019 | sockets | 4 |
b0019 | coffee | 15 |
【问题】编写 SQL 语句,返回 Products 表中所有价格在 3 美元到 6 美元之间的产品的名称(prod_name)和价格(prod_price),然后按价格对结果进行排序
答案:
SELECT prod_name, prod_price
FROM Products
WHERE prod_price BETWEEN 3 AND 6
ORDER BY prod_price
# 或者
SELECT prod_name, prod_price
FROM Products
WHERE prod_price >= 3 AND prod_price <= 6
ORDER BY prod_price
2
3
4
5
6
7
8
9
10
# 返回更多的产品⭐️
OrderItems 表含有:订单号 order_num,quantity 产品数量
order_num | quantity |
---|---|
a1 | 105 |
a2 | 1100 |
a2 | 200 |
a4 | 1121 |
a5 | 10 |
a2 | 19 |
a7 | 5 |
【问题】从 OrderItems 表中检索出所有不同且不重复的订单号(order_num),其中每个订单都要包含 100 个或更多的产品。
答案:
SELECT order_num
FROM OrderItems
GROUP BY order_num
HAVING SUM(quantity) >= 100
2
3
4
知识点:
group by
: 字段进行分组可以实现不同且不重复的结果。HAVING
子句:用于过滤分组后的结果,只返回满足条件的分组,这里是筛选满足SUM(quantity) >= 100
条件的分组。SUM()
函数:用于计算指定字段的总和,这里是计算 quantity 字段的总和。
# 高级数据过滤
# 检索供应商名称
Vendors 表有字段供应商名称(vend_name)、供应商国家(vend_country)、供应商州(vend_state)
vend_name | vend_country | vend_state |
---|---|---|
apple | USA | CA |
vivo | CNA | shenzhen |
huawei | CNA | xian |
【问题】编写 SQL 语句,从 Vendors 表中检索供应商名称(vend_name),仅返回加利福尼亚州的供应商(这需要按国家[USA]和州[CA]进行过滤,没准其他国家也存在一个[CA])
答案:
select vend_name from Vendors where vend_country = 'USA' AND vend_state = 'CA'
注意:
- 字符串需要使用
单引号
或双引号
包裹
# 检索并列出已订购产品的清单⭐️
OrderItems 表包含了所有已订购的产品(有些已被订购多次)。
prod_id | order_num | quantity |
---|---|---|
BR01 | a1 | 105 |
BR02 | a2 | 1100 |
BR02 | a2 | 200 |
BR03 | a4 | 1121 |
BR017 | a5 | 10 |
BR02 | a2 | 19 |
BR017 | a7 | 5 |
【问题】编写 SQL 语句,查找所有订购了数量至少 100 个的 BR01、BR02 或BR03 的订单。你需要返回 OrderItems 表的订单号(order_num)、产品 ID(prod_id)和数量(quantity),并按产品 ID 和数量进行过滤。
答案:
select order_num, prod_id, quantity
from OrderItems
where prod_id in ('BR01', 'BR02','BR03') and quantity >= 100;
2
3
# 返回所有价格在 3 美元到 6 美元之间的产品的名称和价格
有表 Products
prod_id | prod_name | prod_price |
---|---|---|
a0011 | egg | 3 |
a0019 | sockets | 4 |
b0019 | coffee | 15 |
【问题】编写 SQL 语句,返回所有价格在 3 美元到 6 美元之间的产品的名称(prod_name)和价格(prod_price),使用 AND 操作符,然后按价格对结果进行升序排序
答案:
select prod_name, prod_price
from Products
where prod_price between 3 and 6
order by prod_price asc
2
3
4
# 纠错 2
供应商表 Vendors 有字段供应商名称 vend_name、供应商国家 vend_country、供应商省份 vend_state
vend_name | vend_country | vend_state |
---|---|---|
apple | USA | CA |
vivo | CNA | shenzhen |
huawei | CNA | xian |
【问题】修改正确下面 sql,使之正确返回
SELECT vend_name
FROM Vendors
ORDER BY vend_name
WHERE vend_country = 'USA' AND vend_state = 'CA';
2
3
4
答案:
SELECT vend_name
FROM Vendors
WHERE vend_country = 'USA' AND vend_state = 'CA'
ORDER BY vend_name
2
3
4
知识点:考察 sql 关键词的顺序。
order by
的位置位于所有语句的倒数第二,倒数第一是 limit
# 用通配符进行过滤
SQL 通配符必须与 LIKE
运算符一起使用
通配符 | 说明 |
---|---|
% | 用于表示零个或多个字符,可以匹配任意长度的字符串。例如,'abc%' 可以匹配 'abc'、'abcd'、'abcxyz' 等。 |
_ | 用于表示单个字符,可以匹配任意单个字符。例如,'a_c' 可以匹配 'abc'、'adc'、'axc' 等,但不能匹配 'abcd'、'abcde' 等。 |
[ ] | 用于表示字符范围,可以匹配指定范围内的任意一个字符。例如,'[a-z]' 可以匹配任意小写字母。 |
[^ ] | 用于表示字符集的补集,可以匹配不在指定字符集中的任意一个字符。例如,'[^aeiou]' 可以匹配任意非元音字母。 |
[^abc]
:表示匹配除了 a、b、c 以外的任意一个字符。[^a-z]
:表示匹配除了小写字母a到z以外的任意一个字符。[^0-9]
:表示匹配除了数字0到9以外的任意一个字符。[^aeiou]
:表示匹配除了元音字母 a、e、i、o、u 以外的任意一个字母。
# 检索产品名称和描述(一)⭐️
Products 表
prod_name | prod_desc |
---|---|
a0011 | usb |
a0019 | iphone13 |
b0019 | gucci t-shirts |
c0019 | gucci toy |
d0019 | lego toy |
【问题】编写 SQL 语句,从 Products 表中检索产品名称(prod_name)和描述(prod_desc),仅返回描述中包含 toy 一词的产品名称
select prod_name, prod_desc
from Products
where prod_desc like '%toy%'
2
3
# 检索产品名称和描述(二)⭐️
Products 表
prod_name | prod_desc |
---|---|
a0011 | usb |
a0019 | iphone13 |
b0019 | gucci t-shirts |
c0019 | gucci toy |
d0019 | lego toy |
【问题】编写 SQL 语句,从 Products 表中检索产品名称(prod_name)和描述(prod_desc),仅返回描述中未出现 toy 一词的产品,最后按【产品名称】对结果进行排序(默认升序)。
答案:
select prod_name, prod_desc
from Products
where prod_desc not like '%toy%'
order by prod_name
2
3
4
知识点:
not like
不包含
# 检索产品名称和描述(三)⭐️
Products 表
prod_name | prod_desc |
---|---|
a0011 | usb |
a0019 | iphone13 |
b0019 | gucci t-shirts |
c0019 | gucci toy |
d0019 | lego carrots toy |
【问题】编写 SQL 语句,从 Products 表中检索产品名称(prod_name)和描述(prod_desc),仅返回描述中同时出现 toy 和 carrots 的产品。有好几种方法可以执行此操作,但对于这个挑战题,请使用 AND 和两个 LIKE 比较。
答案:
select prod_name, prod_desc
from Products
# where prod_desc like '%carrots%toy%'
where prod_desc like '%toy%' and prod_desc like '%carrots%'
2
3
4
注意:and
的两边都要写上字段名
# 检索产品名称和描述(四)
Products 表
prod_name | prod_desc |
---|---|
a0011 | usb |
a0019 | iphone13 |
b0019 | gucci t-shirts |
c0019 | gucci toy |
d0019 | lego toy carrots |
【问题】编写 SQL 语句,从 Products 表中检索产品名称(prod_name)和描述(prod_desc),仅返回在描述中以先后顺序同时出现 toy 和 carrots 的产品。提示:只需要用带有三个 % 符号的 LIKE 即可。
答案:
select prod_name, prod_desc
from Products
where prod_desc like '%toy%carrots%'
2
3
# 创建计算字段
# 别名
别名的常见用法是在检索出的结果中重命名表的列字段(为了符合特定的报表要求或客户需求)。有表 Vendors 代表供应商信息,vend_id 供应商 id、vend_name 供应商名称、vend_address 供应商地址、vend_city 供应商城市。
vend_id | vend_name | vend_address | vend_city |
---|---|---|---|
a001 | tencent cloud | address1 | shenzhen |
a002 | huawei cloud | address2 | dongguan |
a003 | aliyun cloud | address3 | hangzhou |
a003 | netease cloud | address4 | guangzhou |
【问题】编写 SQL 语句,从 Vendors 表中检索 vend_id、vend_name、vend_address 和 vend_city,将 vend_name 重命名为 vname,将 vend_city 重命名为 vcity,将 vend_address 重命名为 vaddress,按供应商名称对结果进行升序排序。
答案:
select vend_id, vend_name as vname, vend_address as vaddress, vend_city as vcity
from Vendors
order by vname
# as 可以省略
SELECT vend_id, vend_name vname, vend_address vaddress, vend_city vcity
FROM Vendors
ORDER BY vname
2
3
4
5
6
7
8
# 打折⭐️
我们的示例商店正在进行打折促销,所有产品均降价 10%。Products 表包含 prod_id 产品 id、prod_price 产品价格
【问题】编写 SQL 语句,从 Products 表中返回 prod_id、prod_price 和 sale_price。sale_price 是一个包含促销价格的计算字段。提示:可以乘以 0.9,得到原价的 90%(即 10% 的折扣)
答案:
select prod_id, prod_price, prod_price * 0.9 as sale_price
from Products
2
# 使用函数处理数据
# 顾客登录名⭐️
我们的商店已经上线了,正在创建顾客账户。所有用户都需要登录名,默认登录名是其名称和所在城市的组合。
给出 Customers 表如下:
cust_id | cust_name | cust_contact | cust_city |
---|---|---|---|
a1 | Andy Li | Andy Li | Oak Park |
a2 | Ben Liu | Ben Liu | Oak Park |
a3 | Tony Dai | Tony Dai | Oak Park |
a4 | Tom Chen | Tom Chen | Oak Park |
a5 | An Li | An Li | Oak Park |
a6 | Lee Chen | Lee Chen | Oak Park |
a7 | Hex Liu | Hex Liu | Oak Park |
【问题】编写 SQL 语句,返回顾客 ID(cust_id)、顾客名称(cust_name)和登录名(user_login),其中登录名全部为大写字母,并由顾客联系人的前两个字符(cust_contact)和其所在城市的前三个字符(cust_city)组成。提示:需要使用函数、拼接和别名。
答案:
select cust_id, cust_name, upper(concat(substring(cust_contact, 1, 2), substring(cust_city, 1, 3))) as user_login
from Customers
2
知识点:
关键词:substing,concat,upper
用法:
- 字符串的截取:substring(字符串,起始位置,截取字符数)
- 字符串的拼接:concat(字符串1,字符串2,字符串3,...)
- 字母大写:upper(字符串)
# 返回 2020 年 1 月的所有订单的订单号和订单日期⭐️
Orders 订单表
order_num | order_date |
---|---|
a0001 | 2020-01-01 00:00:00 |
a0002 | 2020-01-02 00:00:00 |
a0003 | 2020-01-01 12:00:00 |
a0004 | 2020-02-01 00:00:00 |
a0005 | 2020-03-01 00:00:00 |
【问题】编写 SQL 语句,返回 2020 年 1 月的所有订单的订单号(order_num)和订单日期(order_date),并按订单日期升序排序
答案:
select order_num, order_date
from Orders
where month(order_date) = '01' and year(order_date) = '2020'
order by order_date
2
3
4
知识点:
SQL语法基础小结 | 日期和时间处理 (opens new window)
其他解法
字符串匹配(近似查找法)
用 like 来查找
select order_num, order_date
from Orders
where order_date like '2020-01%'
order by order_date
2
3
4
切割字符串
select order_num, order_date
from Orders
where left(order_date, 7) = '2020-01'
order by order_date
2
3
4
字符串比较
select *
from Orders
where order_date >= '2020-01-01 00:00:00' and order_date <= '2020-01-31 23:59:59'
order by order_date;
2
3
4
用正则来查找(效率不如 like,能用 like 就用 like)
select order_num, order_date
from Orders
where order_date regexp '2020-01'
order by order_date
2
3
4
时间函数匹配
利用date_format (opens new window)函数 (参考其中的匹配规则进行匹配)
select order_num, order_date
from Orders
where date_format(order_date, '%Y-%m')='2020-01'
order by order_date
2
3
4
# 汇总数据
# 确定已售出产品的总数
OrderItems 表代表售出的产品,quantity 代表售出商品数量。
quantity |
---|
10 |
100 |
1000 |
10001 |
2 |
15 |
【问题】编写 SQL 语句,确定已售出产品的总数。返回 items_ordered
列名,表示已售出商品的总数。
答案:
select sum(quantity) as items_ordered
from OrderItems
2
# 确定已售出产品项 BR01 的总数
OrderItems 表代表售出的产品,quantity 代表售出商品数量,产品项为 prod_item。
quantity | prod_id |
---|---|
10 | AR01 |
100 | AR10 |
1000 | BR01 |
10001 | BR010 |
【问题】修改创建的语句,确定已售出产品项(prod_item)为 "BR01" 的总数。
select sum(quantity) as items_ordered
from OrderItems
where prod_id = 'BR01'
2
3
# 确定 Products 表中价格不超过 10 美元的最贵产品的价格
Products 表
prod_price |
---|
9.49 |
600 |
1000 |
【问题】编写 SQL 语句,确定 Products 表中价格不超过 10 美元的最贵产品的价格(prod_price)。将计算所得的字段命名为 max_price。
答案:
select max(prod_price) as max_price
from Products
where prod_price <= 10
2
3
# 分组数据
GROUP BY
:
GROUP BY
子句将记录分组到汇总行中。GROUP BY
为每个组返回一个记录。GROUP BY
通常还涉及聚合COUNT
,MAX
,SUM
,AVG
等。GROUP BY
可以按一列或多列进行分组。GROUP BY
按分组字段进行排序后,ORDER BY
可以以汇总字段来进行排序。
HAVING
:
HAVING
用于对汇总的GROUP BY
结果进行过滤。HAVING
必须要与GROUP BY
连用。WHERE
和HAVING
可以在相同的查询中。
HAVING
vs WHERE
:
WHERE
:过滤指定的行,后面不能加聚合函数(分组函数)。HAVING
:过滤分组,必须要与GROUP BY
连用,不能单独使用。
# 返回每个订单号各有多少行数
OrderItems 表包含每个订单的每个产品
order_num |
---|
a002 |
a002 |
a002 |
a004 |
a007 |
【问题】编写 SQL 语句,返回每个订单号(order_num)各有多少行数(order_lines),并按 order_lines 对结果进行升序排序。
select order_num, count(order_num) as order_lines
from OrderItems
group by order_num
order by order_lines
2
3
4
count(*)
,count(列名)
都可以,区别在于:count(列名)
是统计非 NULL 的行数;
# 每个供应商成本最低的产品
有 Products 表,含有字段 prod_price 代表产品价格,vend_id 代表供应商 id
vend_id | prod_price |
---|---|
a0011 | 100 |
a0019 | 0.1 |
b0019 | 1000 |
b0019 | 6980 |
b0019 | 20 |
【问题】编写 SQL 语句,返回名为 cheapest_item 的字段,该字段包含每个供应商成本最低的产品(使用 Products 表中的 prod_price),然后从最低成本到最高成本对结果进行升序排序。
答案:
select vend_id, min(prod_price) as cheapest_item
from Products
group by vend_id
order by cheapest_item
2
3
4
# 返回订单数量总和不小于 100 的所有订单的订单号
OrderItems 代表订单商品表,包括:订单号 order_num 和订单数量 quantity。
order_num | quantity |
---|---|
a1 | 105 |
a2 | 1100 |
a2 | 200 |
a4 | 1121 |
a5 | 10 |
a2 | 19 |
a7 | 5 |
【问题】请编写 SQL 语句,返回订单数量总和不小于 100 的所有订单号,最后结果按照订单号升序排序。
答案:
select order_num
from OrderItems
group by order_num
having sum(quantity) >= 100
order by order_num
2
3
4
5
# 计算总和
OrderItems 表代表订单信息,包括字段:订单号 order_num 和 item_price 商品售出价格、 quantity 商品数量。
order_num | item_price | quantity |
---|---|---|
a1 | 10 | 105 |
a2 | 1 | 1100 |
a2 | 1 | 200 |
a4 | 2 | 1121 |
a5 | 5 | 10 |
a2 | 1 | 19 |
a7 | 7 | 5 |
【问题】编写 SQL 语句,根据订单号聚合,返回订单总价不小于 1000 的所有订单号,最后的结果按订单号进行升序排序。
select order_num, sum(item_price * quantity) as total_priceice
from OrderItems
group by order_num
having sum(item_price * quantity) >= 1000
order by order_num
2
3
4
5
# 纠错 3⭐️
OrderItems 表含有 order_num 订单号
order_num |
---|
a002 |
a002 |
a002 |
a004 |
a007 |
【问题】将下面代码修改正确后执行
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY items
HAVING COUNT(*) >= 3
ORDER BY items, order_num;
2
3
4
5
答案:
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
# HAVING COUNT(*) >= 3 这个也可
HAVING items >= 3
ORDER BY items, order_num;
2
3
4
5
6
知识点:执行顺序问题,别名的使用规则
- SQL 查询的执行顺序是先执行
FROM
、WHERE
、GROUP BY
、HAVING
和最后是SELECT
。
SELECT order_num, COUNT(*) AS items FROM OrderItems GROUP BY order_num HAVING items >= 3 ORDER BY items, order_num;
这个使用别名不报错;
select order_num, sum(item_price * quantity) as total_price from OrderItems group by order_num having total_price >= 1000 order by order_num
但为什么这个使用别名就会报错
- 在 SQL 查询中,使用别名通常分为两种情况:
- 使用别名在
SELECT
子句中展示计算结果:这种情况下,别名可以直接在其他子句中使用,如在GROUP BY
、HAVING
、ORDER BY
等子句中使用别名进行条件过滤或排序。这就是为什么你的第一个查询中使用别名items
在HAVING
子句中不会报错的原因。 - 使用别名在
HAVING
子句中进行条件过滤:这种情况下,数据库管理系统不会识别别名,因为HAVING
子句是在GROUP BY
子句之后执行的,此时数据库还没有计算别名对应的结果。所以在HAVING
子句中使用别名进行条件过滤是会报错的。
# 使用子查询
# 返回购买价格为 10 美元或以上产品的顾客列表
OrderItems 表示订单商品表,含有字段订单号:order_num、订单价格:item_price;Orders表代表订单信息表,含有顾客id:cust_id 和订单号:order_num
OrderItems 表
order_num | item_price |
---|---|
a1 | 10 |
a2 | 1 |
a2 | 1 |
a4 | 2 |
a5 | 5 |
a2 | 1 |
a7 | 7 |
Orders 表
order_num | cust_id |
---|---|
a1 | cust10 |
a2 | cust1 |
a2 | cust1 |
a4 | cust2 |
a5 | cust5 |
a2 | cust1 |
a7 | cust7 |
【问题】使用子查询,返回购买价格为 10 美元或以上产品的顾客列表,结果无需排序。 注意:你需要使用 OrderItems 表查找匹配的订单号(order_num),然后使用 Order 表检索这些匹配订单的顾客 ID(cust_id)。
答案:
select cust_id
from Orders
where order_num in (select order_num
from OrderItems
group by order_num
having sum(item_price) >= 10)
2
3
4
5
6
# 确定哪些订单购买了 prod_id 为 BR01 的产品(一)
表 OrderItems 代表订单商品信息表,prod_id 为产品 id;Orders 表代表订单表有 cust_id 代表顾客 id 和订单日期 order_date
OrderItems 表
prod_id | order_num |
---|---|
BR01 | a0001 |
BR01 | a0002 |
BR02 | a0003 |
BR02 | a0013 |
Orders 表
order_num | cust_id | order_date |
---|---|---|
a0001 | cust10 | 2022-01-01 00:00:00 |
a0002 | cust1 | 2022-01-01 00:01:00 |
a0003 | cust1 | 2022-01-02 00:00:00 |
a0013 | cust2 | 2022-01-01 00:20:00 |
【问题】
编写 SQL 语句,使用子查询来确定哪些订单(在 OrderItems 中)购买了 prod_id 为 "BR01" 的产品,然后从 Orders 表中返回每个产品对应的顾客 ID(cust_id)和订单日期(order_date),按订购日期对结果进行升序排序。
答案:
select cust_id, order_date
from Orders
where order_num in (select order_num
from OrderItems
where prod_id = 'BR01')
2
3
4
5
# 返回购买 prod_id 为 BR01 的产品的所有顾客的电子邮件(一)
你想知道订购 BR01 产品的日期,有表 OrderItems 代表订单商品信息表,prod_id 为产品 id;Orders 表代表订单表有 cust_id 代表顾客 id 和订单日期 order_date;Customers 表含有 cust_email 顾客邮件和 cust_id 顾客 id
OrderItems 表
prod_id | order_num |
---|---|
BR01 | a0001 |
BR01 | a0002 |
BR02 | a0003 |
BR02 | a0013 |
Orders 表
order_num | cust_id | order_date |
---|---|---|
a0001 | cust10 | 2022-01-01 00:00:00 |
a0002 | cust1 | 2022-01-01 00:01:00 |
a0003 | cust1 | 2022-01-02 00:00:00 |
a0013 | cust2 | 2022-01-01 00:20:00 |
Customers 表代表顾客信息,cust_id 为顾客 id,cust_email 为顾客 email
【问题】返回购买 prod_id 为 BR01 的产品的所有顾客的电子邮件(Customers 表中的 cust_email),结果无需排序。
提示:这涉及 SELECT 语句,最内层的从 OrderItems 表返回 order_num,中间的从 Customers 表返回 cust_id。
答案:
# 写法 1:子查询
select cust_email
from Customers
where cust_id in (select cust_id
from Orders
where order_num in (select order_num
from OrderItems
where prod_id = 'BR01'))
# 写法 2: 连接表(inner join)
SELECT c.cust_email
FROM OrderItems a,Orders b,Customers c
WHERE a.order_num = b.order_num AND b.cust_id = c.cust_id AND a.prod_id = 'BR01'
# 写法 3:连接表(left join)
SELECT c.cust_email
FROM Orders a LEFT JOIN
OrderItems b ON a.order_num = b.order_num LEFT JOIN
Customers c ON a.cust_id = c.cust_id
WHERE b.prod_id = 'BR01'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 返回每个顾客不同订单的总金额⭐️
我们需要一个顾客 ID 列表,其中包含他们已订购的总金额。
OrderItems 表代表订单信息,OrderItems 表有订单号:order_num 和商品售出价格:item_price、商品数量:quantity。
order_num | item_price | quantity |
---|---|---|
a0001 | 10 | 105 |
a0002 | 1 | 1100 |
a0002 | 1 | 200 |
a0013 | 2 | 1121 |
a0003 | 5 | 10 |
a0003 | 1 | 19 |
a0003 | 7 | 5 |
Orders 表订单号:order_num、顾客 id:cust_id
order_num | cust_id |
---|---|
a0001 | cust10 |
a0002 | cust1 |
a0003 | cust1 |
a0013 | cust2 |
【问题】
编写 SQL 语句,返回顾客 ID(Orders 表中的 cust_id),并使用子查询返回 total_ordered 以便返回每个顾客的订单总数,将结果按金额从大到小排序。
提示:你之前已经使用 SUM() 计算订单总数。
答案:
# 写法 1:子查询
select o.cust_id as cust_id, tb.total_ordered as total_ordered
from (select order_num, sum(item_price * quantity) as total_ordered
from OrderItems
group by order_num) as tb,
Orders o
where tb.order_num = o.order_num
order by total_ordered desc
# 写法 2:连接表
select b.cust_id, sum(a.quantity * a.item_price) as total_ordered
from OrderItems a, Orders b
where a.order_num = b.order_num
group by cust_id
order by total_ordered desc
2
3
4
5
6
7
8
9
10
11
12
13
14
15
知识点:
- 隐式内连接用
where
- 显示内连接用
on
# 从 Products 表中检索所有的产品名称以及对应的销售总数
Products 表中检索所有的产品名称:prod_name、产品 id:prod_id
prod_id | prod_name |
---|---|
a0001 | egg |
a0002 | sockets |
a0013 | coffee |
a0003 | cola |
OrderItems 代表订单商品表,订单产品:prod_id、售出数量:quantity
prod_id | quantity |
---|---|
a0001 | 105 |
a0002 | 1100 |
a0002 | 200 |
a0013 | 1121 |
a0003 | 10 |
a0003 | 19 |
a0003 | 5 |
【问题】
编写 SQL 语句,从 Products 表中检索所有的产品名称(prod_name),以及名为 quant_sold 的计算列,其中包含所售产品的总数(在 OrderItems 表上使用子查询和 SUM(quantity) 检索)。
答案:
# 写法 1:子查询
SELECT p.prod_name, tb.quant_sold
FROM (SELECT prod_id, Sum(quantity) AS quant_sold
FROM OrderItems
GROUP BY prod_id) AS tb,
Products p
WHERE tb.prod_id = p.prod_id
# 写法 2:连接表
SELECT p.prod_name, Sum(o.quantity) AS quant_sold
FROM Products p,
OrderItems o
WHERE p.prod_id = o.prod_id
GROUP BY p.prod_name(这里不能用 p.prod_id,会报错)
2
3
4
5
6
7
8
9
10
11
12
13
14
# 联结表
# 返回顾客名称和相关订单号
Customers 表有字段顾客名称cust_name、顾客id cust_id
cust_id | cust_name |
---|---|
cust10 | andy |
cust1 | ben |
cust2 | tony |
cust22 | tom |
cust221 | an |
cust2217 | hex |
Orders订单信息表,含有字段order_num订单号、cust_id顾客id
order_num | cust_id |
---|---|
a1 | cust10 |
a2 | cust1 |
a3 | cust2 |
a4 | cust22 |
a5 | cust221 |
a7 | cust2217 |
【问题】
编写 SQL 语句,返回 Customers 表中的顾客名称(cust_name)和 Orders 表中的相关订单号(order_num),并按顾客名称再按订单号对结果进行升序排序。你可以尝试用两个不同的写法,一个使用简单的等联结语法,另外一个使用 INNER JOIN。
# 方法一
select c.cust_name, o.order_num
from Customers c, Orders o
where c.cust_id = o.cust_id
order by c.cust_name
# 方法二:inner join
select c.cust_name, o.order_num
from Customers c join Orders o
on c.cust_id = o.cust_id
order by c.cust_name
2
3
4
5
6
7
8
9
10
11
# 返回顾客名称和相关订单号以及每个订单的总价⭐️
Customers 表有字段,顾客名称:cust_name、顾客 id:cust_id
cust_id | cust_name |
---|---|
cust10 | andy |
cust1 | ben |
cust2 | tony |
cust22 | tom |
cust221 | an |
cust2217 | hex |
Orders 订单信息表,含有字段,订单号:order_num、顾客 id:cust_id
order_num | cust_id |
---|---|
a1 | cust10 |
a2 | cust1 |
a3 | cust2 |
a4 | cust22 |
a5 | cust221 |
a7 | cust2217 |
OrderItems 表有字段,商品订单号:order_num、商品数量:quantity、商品价格:item_price
order_num | quantity | item_price |
---|---|---|
a1 | 1000 | 10 |
a2 | 200 | 10 |
a3 | 10 | 15 |
a4 | 25 | 50 |
a5 | 15 | 25 |
a7 | 7 | 7 |
【问题】除了返回顾客名称和订单号,返回 Customers 表中的顾客名称(cust_name)和 Orders 表中的相关订单号(order_num),添加第三列 OrderTotal,其中包含每个订单的总价,并按顾客名称再按订单号对结果进行升序排序。
答案:
# 等连接语法
select c.cust_name, o.order_num, sum(o2.quantity * o2.item_price) as OrderTotal
from Customers c, Orders o, OrderItems o2
where c.cust_id = o.cust_id and o.order_num = o2.order_num
group by c.cust_name, o.order_num
order by c.cust_name, o.order_num
2
3
4
5
6
知识点:
- 分组:通过在
GROUP BY
子句中使用c.cust_name, o.order_num
来指定按Customers
表中的cust_name
和Orders
表中的order_num
对查询结果进行分组。这样,查询结果将按照顾客姓名和订单号进行分组,每个分组对应一个顾客的一个订单。(唯一的)
分组的目的是将具有相同 cust_name
和 order_num
字段值的数据行归为一组,并对每个组内的数据进行聚合操作。
一句话,select 中的字段要么都聚类,要么都不聚类
是否要分组取决于查询的需求。
如果不需要对数据进行聚合操作或按照某些字段进行统计计算,那么可以不使用分组。但在这个查询中,由于需要计算每个订单的总金额,所以需要按照 cust_name
和 order_num
字段进行分组。如果不使用 GROUP BY
,查询结果将会是没有经过聚合计算的所有数据行,而不是按订单进行汇总后的结果。
# 确定哪些订单购买了 prod_id 为 BR01 的产品(二)
表 OrderItems 代表订单商品信息表,prod_id 为产品 id;Orders 表代表订单表有 cust_id 代表顾客 id 和订单日期 order_date
OrderItems 表
prod_id | order_num |
---|---|
BR01 | a0001 |
BR01 | a0002 |
BR02 | a0003 |
BR02 | a0013 |
Orders 表
order_num | cust_id | order_date |
---|---|---|
a0001 | cust10 | 2022-01-01 00:00:00 |
a0002 | cust1 | 2022-01-01 00:01:00 |
a0003 | cust1 | 2022-01-02 00:00:00 |
a0013 | cust2 | 2022-01-01 00:20:00 |
【问题】编写 SQL 语句,使用子查询来确定哪些订单(在 OrderItems 中)购买了 prod_id 为 "BR01" 的产品,然后从 Orders 表中返回每个产品对应的顾客 ID(cust_id)和订单日期(order_date),按订购日期对结果进行升序排序。
答案:
select cust_id, order_date
from Orders
where order_num in (select order_num
from OrderItems
where prod_id = 'BR01')
order by order_date
2
3
4
5
6
# 返回购买 prod_id 为 BR01 的产品的所有顾客的电子邮件(二)
有表 OrderItems 代表订单商品信息表,prod_id 为产品 id;Orders 表代表订单表有 cust_id 代表顾客 id 和订单日期 order_date;Customers 表含有 cust_email 顾客邮件和 cust_id 顾客 id
OrderItems 表
prod_id | order_num |
---|---|
BR01 | a0001 |
BR01 | a0002 |
BR02 | a0003 |
BR02 | a0013 |
Orders表
order_num | cust_id | order_date |
---|---|---|
a0001 | cust10 | 2022-01-01 00:00:00 |
a0002 | cust1 | 2022-01-01 00:01:00 |
a0003 | cust1 | 2022-01-02 00:00:00 |
a0013 | cust2 | 2022-01-01 00:20:00 |
Customers 表代表顾客信息,cust_id 为顾客 id,cust_email 为顾客 email
【问题】返回购买 prod_id 为 BR01 的产品的所有顾客的电子邮件(Customers 表中的 cust_email),结果无需排序。
提示:涉及到 SELECT 语句,最内层的从 OrderItems 表返回 order_num,中间的从 Customers 表返回 cust_id,但是必须使用 INNER JOIN 语法。(我看未必)
select cust_email
from Customers
where cust_id in (select cust_id
from Orders
where order_num in (select order_num
from OrderItems
where prod_id = 'BR01'))
2
3
4
5
6
7
# 确定最佳顾客的另一种方式(二)⭐️
OrderItems 表代表订单信息,确定最佳顾客的另一种方式是看他们花了多少钱,OrderItems 表有订单号 order_num 和 item_price 商品售出价格、quantity 商品数量
order_num | item_price | quantity |
---|---|---|
a1 | 10 | 105 |
a2 | 1 | 1100 |
a2 | 1 | 200 |
a4 | 2 | 1121 |
a5 | 5 | 10 |
a2 | 1 | 19 |
a7 | 7 | 5 |
Orders 表含有字段 order_num 订单号、cust_id 顾客 id
order_num | cust_id |
---|---|
a1 | cust10 |
a2 | cust1 |
a3 | cust2 |
a4 | cust22 |
a5 | cust221 |
a7 | cust2217 |
顾客表 Customers 有字段 cust_id 客户 id、cust_name 客户姓名
cust_id | cust_name |
---|---|
cust10 | andy |
cust1 | ben |
cust2 | tony |
cust22 | tom |
cust221 | an |
cust2217 | hex |
【问题】编写 SQL 语句,返回订单总价不小于1000 的客户名称和总额(OrderItems 表中的order_num)。
提示:需要计算总和(item_price 乘以 quantity)。按总额对结果进行排序,请使用 INNER JOIN 语法。
答案:
select cust_name, sum(item_price * quantity) as total_price
from Customers
inner join Orders using(cust_id)
inner join OrderItems using(order_num)
group by cust_name
having total_price >= 1000
order by total_price
2
3
4
5
6
7
知识点:
通过 using
关键字,根据 cust_id
将 Customers
表和 Orders
表连接,根据 order_num
将 Orders
表和 OrderItems
表连接。
using 与 on 区别
using
和 on
是在 SQL 中用于连接表的两种不同方式。
using
:
using
是一种简化的连接语法,用于连接两个表,并指定连接条件的字段。它适用于连接那些具有相同名称的字段的表。- 在使用
using
进行连接时,只需在连接的表名之间使用using
关键字,并在后面紧跟需要进行连接的字段名,这些字段必须在两个表中都存在且名称相同。 - 例如:
INNER JOIN table2 using(column_name)
。
on
:
on
是一种更灵活的连接语法,用于连接两个表,并指定连接条件的具体条件。它适用于连接那些具有不同名称或者复杂条件的字段的表。- 在使用
on
进行连接时,需要在on
关键字后面写入连接条件,连接条件可以是简单的等值条件,也可以是更复杂的逻辑条件。 - 例如:
INNER JOIN table2 on table1.column_name = table2.column_name
。
总结:
using
适用于连接具有相同名称的字段的表,它简洁且适用于某些特定场景。on
更灵活,适用于连接具有不同名称或复杂条件的字段的表,它提供了更多的连接方式和条件控制。
# 创建高级联结
# 检索每个顾客的名称和所有的订单号(一)
Customers 表代表顾客信息含有顾客 id cust_id 和 顾客名称 cust_name
cust_id | cust_name |
---|---|
cust10 | andy |
cust1 | ben |
cust2 | tony |
cust22 | tom |
cust221 | an |
cust2217 | hex |
Orders表代表订单信息含有订单号order_num和顾客id cust_id
order_num | cust_id |
---|---|
a1 | cust10 |
a2 | cust1 |
a3 | cust2 |
a4 | cust22 |
a5 | cust221 |
a7 | cust2217 |
【问题】使用 INNER JOIN 编写 SQL 语句,检索每个顾客的名称(Customers 表中的 cust_name)和所有的订单号(Orders 表中的 order_num),最后根据顾客姓名 cust_name 升序返回。
答案:
select cust_name, order_num
from Customers
inner join Orders using(cust_id)
order by cust_name
2
3
4
# 检索每个顾客的名称和所有的订单号(二)⭐️
Orders 表代表订单信息含有订单号 order_num 和顾客 id:cust_id
order_num | cust_id |
---|---|
a1 | cust10 |
a2 | cust1 |
a3 | cust2 |
a4 | cust22 |
a5 | cust221 |
a7 | cust2217 |
Customers 表代表顾客信息含有顾客 id cust_id 和顾客名称 cust_name
cust_id | cust_name |
---|---|
cust10 | andy |
cust1 | ben |
cust2 | tony |
cust22 | tom |
cust221 | an |
cust2217 | hex |
cust40 | ace |
【问题】检索每个顾客的名称(Customers 表中的 cust_name)和所有的订单号(Orders 表中的 order_num),列出所有的顾客,即使他们没有下过订单。最后根据顾客姓名 cust_name 升序返回。
select cust_name,order_num
from Customers
left join Orders using(cust_id)
order by cust_name;
2
3
4
知识点:
内联结:inner join。取两列的交集。
外联结:
left join。左连接,以左边表的列为主,取两列的交集,对于不在右边列存在的名称取 null。
right join。右连接,以右边表的列为主,取两列的交集,对于不在左边列存在的名称取 null。
# 返回产品名称和与之相关的订单号
Products 表为产品信息表含有字段 prod_id 产品 id、prod_name 产品名称
prod_id | prod_name |
---|---|
a0001 | egg |
a0002 | sockets |
a0013 | coffee |
a0003 | cola |
a0023 | soda |
OrderItems 表为订单信息表含有字段 order_num 订单号和产品 id prod_id
prod_id | order_num |
---|---|
a0001 | a105 |
a0002 | a1100 |
a0002 | a200 |
a0013 | a1121 |
a0003 | a10 |
a0003 | a19 |
a0003 | a5 |
【问题】使用 OUTER JOIN 联结 Products 表和 OrderItems 表,返回产品名称(prod_name)和与之相关的订单号(order_num)的列表,并按照产品名称升序排序。
答案:
select prod_name,order_num
from Products
left join OrderItems using(prod_id)
order by prod_name;
2
3
4
# 返回产品名称和每一项产品的总订单数
Products 表为产品信息表含有字段 prod_id 产品 id、prod_name 产品名称
prod_id | prod_name |
---|---|
a0001 | egg |
a0002 | sockets |
a0013 | coffee |
a0003 | cola |
a0023 | soda |
OrderItems 表为订单信息表含有字段 order_num 订单号和产品 id prod_id
prod_id | order_num |
---|---|
a0001 | a105 |
a0002 | a1100 |
a0002 | a200 |
a0013 | a1121 |
a0003 | a10 |
a0003 | a19 |
a0003 | a5 |
【问题】使用 OUTER JOIN 联结 Products 表和 OrderItems 表,返回产品名称(prod_name)和每一项产品的总订单数(不是订单号),并按产品名称升序排序。
答案:
# select prod_name, count(order_num) as orders
select prod_name, count(o.prod_id) as orders
from Products
left join OrderItems o using(prod_id)
group by prod_name
order by prod_name;
2
3
4
5
6
知识点:
使用 COUNT() 聚合函数计算每个产品的总订单数,并将结果作为 total_orders 列的值返回。
有聚合函数就要分组。
# 列出供应商及其可供产品的数量
有 Vendors 表含有 vend_id 供应商 id.
vend_id |
---|
a0002 |
a0013 |
a0003 |
a0010 |
有 Products 表含有供应商 id 和供应产品 id
vend_id | prod_id |
---|---|
a0001 | egg |
a0002 | prod_id_iphone |
a00113 | prod_id_tea |
a0003 | prod_id_vivo phone |
a0010 | prod_id_huawei phone |
【问题】
列出供应商(Vendors 表中的 vend_id)及其可供产品的数量,包括没有产品的供应商。你需要使用 OUTER JOIN 和 COUNT() 聚合函数来计算 Products 表中每种产品的数量,最后根据 vend_id 升序排序。
注意:vend_id 列会显示在多个表中,因此在每次引用它时都需要完全限定它。
答案:
select v.vend_id, count(p.prod_id) as prod_id
from Vendors v
left join Products p using(vend_id)
group by v.vend_id
order by v.vend_id;
2
3
4
5
# 组合查询
# 将两个 SELECT 语句结合起来(一)
表 OrderItems 包含订单产品信息,字段 prod_id 代表产品 id、quantity 代表产品数量
prod_id | quantity |
---|---|
a0001 | 105 |
a0002 | 100 |
a0002 | 200 |
a0013 | 1121 |
a0003 | 10 |
a0003 | 19 |
a0003 | 5 |
BNBG | 10002 |
【问题】将两个 SELECT 语句结合起来,以便从 OrderItems 表中检索产品 id(prod_id)和 quantity。其中,一个 SELECT 语句过滤数量为 100 的行,另一个 SELECT 语句过滤 id 以 BNBG 开头的产品,最后按产品 id 对结果进行升序排序。
答案:
select prod_id, quantity
from OrderItems
where quantity = 100
union
select prod_id, quantity
from OrderItems
where prod_id like 'BNBG%'
2
3
4
5
6
7
知识点:
UNION 组合 | DreamRain (cmty256.github.io) (opens new window)
# 将两个 SELECT 语句结合起来(二)
表 OrderItems 包含订单产品信息,字段 prod_id 代表产品 id、quantity 代表产品数量。
prod_id | quantity |
---|---|
a0001 | 105 |
a0002 | 100 |
a0002 | 200 |
a0013 | 1121 |
a0003 | 10 |
a0003 | 19 |
a0003 | 5 |
BNBG | 10002 |
【问题】将两个 SELECT 语句结合起来,以便从 OrderItems 表中检索产品 id(prod_id)和 quantity。其中,一个 SELECT 语句过滤数量为 100 的行,另一个 SELECT 语句过滤 id 以 BNBG 开头的产品,最后按产品 id 对结果进行升序排序。 注意:这次仅使用单个 SELECT 语句。
【示例结果】
返回产品id prod_id 和产品数量 quantity
prod_id | quantity |
---|---|
a0002 | 100 |
BNBG | 10002 |
答案:
select prod_id, quantity
from OrderItems
where quantity = 100 or prod_id like 'BNBG%'
2
3
# 组合 Products 表中的产品名称和 Customers 表中的顾客名称⭐️
Products 表含有字段 prod_name 代表产品名称
prod_name |
---|
flower |
rice |
ring |
umbrella |
Customers 表代表顾客信息,cust_name 代表顾客名称
cust_name |
---|
andy |
ben |
tony |
tom |
an |
lee |
hex |
【问题】
编写 SQL 语句,组合 Products 表中的产品名称(prod_name)和 Customers 表中的顾客名称(cust_name)并返回,然后按产品名称对结果进行升序排序。
答案:
# UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名.
select prod_name
from Products
union
select cust_name
from Customers
order by prod_name
2
3
4
5
6
7
# 纠错 4
表 Customers 含有字段 cust_name 顾客名、cust_contact 顾客联系方式、cust_state 顾客州、cust_email 顾客 email
【问题】修正下面错误的 SQL
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state = 'MI'
ORDER BY cust_name;
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state = 'IL' ORDER BY cust_name;
2
3
4
5
6
7
8
答案:
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state = 'MI'
UNION
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state = 'IL'
ORDER BY cust_name;
2
3
4
5
6
7
8
错误点:写了两个 order by
知识点:
使用 union 组合查询时,只能使用一条 order by 子句,他必须位于最后一条 select 语句之后,因为对于【结果集】不存在对于一部分数据进行排序,而另一部分用另一种排序规则的情况。