牛客题霸:必知必会
题目来源:
# 检索数据
# SQL1 从 Customers 表中检索所有的 ID
现有表Customers如下:
cust_id |
---|
A |
B |
C |
编写 SQL 语句,从 Customers 表中检索所有的 cust_id
select cust_id
from Customers;
# SQL2 检索并列出已订购产品的清单
表OrderItems含有非空的列prod_id代表商品id,包含了所有已订购的商品(有些已被订购多次)。
prod_id |
---|
a1 |
a2 |
a3 |
a4 |
a5 |
a6 |
a7 |
编写 SQL 语句,检索并列出所有已订购商品(prod_id)的去重后的清单。
select distinct prod_id
from OrderItems;
# SQL3 检索所有列
现在有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;
# 排序检索数据
# SQL4 检索顾客名称并且排序
有表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
# 根据列索引位置(索引下标从 1 开始)
select cust_name
from Customers
order by 1 desc
# SQL5 对顾客 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;
# 根据列索引位置
select cust_id, order_num
from Orders
order by 0, 2 desc;
# SQL6 按照数量和价格排序
假设有一个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;
# 根据列索引位置
select quantity, item_price
from OrderItems
order by 1 desc, 2 desc;
# SQL7 检查 SQL 语句
有Vendors表
vend_name |
---|
海底捞 |
小龙坎 |
大龙燚 |
下面的 SQL 语句有问题吗?尝试将它改正确,使之能够正确运行,并且返回结果根据 vend_name 逆序排列
SELECT vend_name,
FROM Vendors
ORDER vend_name DESC;
select vend_name
from Vendors
order by vend_name desc;
# 过滤数据
# SQL8 返回固定价格的产品
有表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;
# SQL9 返回更高价格的产品
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;
# SQL10 返回产品并且按照价格排序
有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;
# SQL11 返回更多的产品
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 distinct order_num
from OrderItems
where quantity >= 100;
# 高级数据过滤
# SQL12 检索供应商名称
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';
# SQL13 检索并列出已订购产品的清单
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 quantity >= 100
and prod_id in('BR01', 'BR02', 'BR03');
# SQL14 返回所有价格在 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;
# SQL15 纠错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';
修改后:
select vend_name
from Vendors
where vend_country = 'USA' and vend_state = 'CA'
order by vend_name;
order
语句必须放在 where 之后
# 用通配符进行过滤
SQL 通配符必须与 LIKE
运算符一起使用
在 SQL 中,可使用以下通配符:
通配符 | 描述 |
---|---|
% | 代表零个或多个字符 |
_ | 仅替代一个字符 |
[charlist] | 字符列中的任何单一字符 |
[^charlist] 或者 [!charlist] | 不在字符列中的任何单一字符 |
# SQL16 检索产品名称和描述(一)
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%';
# SQL17 检索产品名称和描述(二)
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;
# SQL18 检索产品名称和描述(三)
Products表
【问题】编写 SQL 语句,从 Products 表中检索产品名称(prod_name)和描述(prod_desc),仅返回描述中同时出现 toy 和 carrots 的产品。有好几种方法可以执行此操作,但对于这个挑战题,请使用 AND 和两个 LIKE 比较。
prod_name | prod_desc |
---|---|
a0011 | usb |
a0019 | iphone13 |
b0019 | gucci t-shirts |
c0019 | gucci toy |
d0019 | lego carrots toy |
select prod_name, prod_desc
from Products
where prod_desc like '%toy%'
and prod_desc like "%carrots%";
# SQL19 检索产品名称和描述(四)
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%';
# 创建计算字段
# SQL20 别名
别名的常见用法是在检索出的结果中重命名表的列字段(为了符合特定的报表要求或客户需求)。有表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;
# SQL21 打折
我们的示例商店正在进行打折促销,所有产品均降价 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;
# 使用函数处理数据
# SQL22 顾客登录名
我们的商店已经上线了,正在创建顾客账户。所有用户都需要登录名,默认登录名是其名称和所在城市的组合。
给出 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;
这道题有三个关键点:
截取函数:
substring()
用法:SUBSTRING(str ,n ,m):返回字符串 str 从第 n 个字符截取到第 m 个字符(左闭右闭);
拼接函数:
concat()
用法:select concat(A,B) 或者select A || B
大写函数
UPPER()
# SQL23 返回 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;
也可以用通配符来做:
select order_num, order_date
from Orders
where order_date like '2020-01%'
order by order_date;
# 汇总数据
# SQL24 确定已售出产品的总数
OrderItems表代表售出的产品,quantity代表售出商品数量。
quantity |
---|
10 |
100 |
1000 |
10001 |
2 |
15 |
【问题】编写 SQL 语句,确定已售出产品的总数。
select sum(quantity) as items_ordered
from OrderItems;
# SQL25 确定已售出产品项 BR01 的总数
OrderItems表代表售出的产品,quantity代表售出商品数量,产品项为prod_id。
quantity | prod_id |
---|---|
10 | AR01 |
100 | AR10 |
1000 | BR01 |
10001 | BR010 |
【问题】修改创建的语句,确定已售出产品项(prod_id)为"BR01"的总数。
select sum(quantity) as items_ordered
from OrderItems
where prod_id = 'BR01';
# SQL26 确定 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;
# 分组数据 group by...having
where
是基于行过滤group by..... having...
是基于分组过滤
GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组。
SQL GROUP BY 语法:
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name
SQL GROUP BY 实例:
我们拥有下面这个 "Orders" 表:
O_Id | OrderDate | OrderPrice | Customer |
---|---|---|---|
1 | 2008/12/29 | 1000 | Bush |
2 | 2008/11/23 | 1600 | Carter |
3 | 2008/10/05 | 700 | Bush |
4 | 2008/09/28 | 300 | Bush |
5 | 2008/08/06 | 2000 | Adams |
6 | 2008/07/21 | 100 | Carter |
现在,我们希望查找每个客户的总金额(总订单)。
我们想要使用 GROUP BY 语句对客户进行组合。
我们使用下列 SQL 语句:
SELECT Customer,SUM(OrderPrice) FROM Orders
GROUP BY Customer
结果集类似这样:
Customer | SUM(OrderPrice) |
---|---|
Bush | 2000 |
Carter | 1700 |
Adams | 2000 |
很棒吧,对不对?
让我们看一下如果省略 GROUP BY 会出现什么情况:
SELECT Customer,SUM(OrderPrice) FROM Orders
结果集类似这样:
Customer | SUM(OrderPrice) |
---|---|
Bush | 5700 |
Carter | 5700 |
Bush | 5700 |
Bush | 5700 |
Adams | 5700 |
Carter | 5700 |
上面的结果集不是我们需要的。
那么为什么不能使用上面这条 SELECT 语句呢?解释如下:
上面的 SELECT 语句指定了两列(Customer 和 SUM(OrderPrice))。"SUM(OrderPrice)
" 返回一个单独的值("OrderPrice" 列的总计),而 "Customer
" 返回 6 个值(每个值对应 "Orders" 表中的每一行)。因此,我们得不到正确的结果。不过,您已经看到了,GROUP BY 语句解决了这个问题。
我们也可以对一个以上的列应用 GROUP BY 语句,就像这样:
SELECT Customer,OrderDate,SUM(OrderPrice) FROM Orders
GROUP BY Customer,OrderDate
# SQL27 返回每个订单号各有多少行数
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;
# SQL28 每个供应商成本最低的产品
有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;
# SQL29 返回订单数量总和不小于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;
group by...... having......
,group by...... where......
是语法错误
# SQL30 计算总和
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 的所有订单号,最后的结果按订单号进行升序排序。
提示:总价 = item_price 乘以 quantity
select order_num, sum(item_price * quantity) as total_price
from OrderItems
group by order_num
having total_price >= 1000
order by order_num;
# SQL31 纠错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;
修改后
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING items >= 3
ORDER BY items, order_num;
# 使用子查询
子查询也称“内部查询”或者“嵌套查询”,是指将一个 SELECT 查询(子查询)的结果作为另一个 SQL 语句(主查询)的数据来源或者判断条件。
子查询可以嵌入 SELECT、INSERT、UPDATE 和 DELETE 语句中,也可以和 =、<、>、IN、BETWEEN、EXISTS 等运算符一起使用。
子查询常用在 WHERE 子句和 FROM 子句后边:
- 当用于 WHERE 子句时,根据不同的运算符,子查询可以返回单行单列、多行单列、单行多列数据。子查询就是要返回能够作为 WHERE 子句查询条件的值。
- 当用于 FROM 子句时,一般返回多行多列数据,相当于返回一张临时表,这样才符合 FROM 后面是表的规则。这种做法能够实现多表联合查询。
注意,MySQL 数据库从 4.1 版本才开始支持子查询,早期版本是不支持的。
用于 WHERE 子句的子查询的基本语法如下:
SELECT column_name [, column_name ]
FROM table1 [, table2 ]
WHERE column_name OPERATOR
(SELECT column_name [, column_name ]
FROM table1 [, table2 ]
[WHERE])
子查询需要放在括号( )
内。OPERATOR 表示用于 WHERE 子句的运算符。
用于 FROM 子句的子查询的基本语法如下:
SELECT column_name [, column_name ]
FROM (SELECT column_name [, column_name ]
FROM table1 [, table2 ]
[WHERE]) AS temp_table_name
WHERE condition
用于 FROM 的子查询返回的结果相当于一张临时表,所以需要使用 AS 关键字为该临时表起一个名字。
# SQL32 返回购买价格为 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 美元或以上产品的顾客列表,结果无需排序。
select cust_id
from Orders
where order_num in (
select order_num
from OrderItems
group by order_num
having sum(item_price) >= 10
);
# SQL33 确定哪些订单购买了 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),按订购日期对结果进行升序排序。
# 写法 1:子查询
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: 连接表
select
b.cust_id, b.order_date
from
OrderItems a, Orders b
where
a.order_num = b.order_num
and a.prod_id = 'BR01'
order by
order_date;
# SQL34 返回购买 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
cust_id | cust_email |
---|---|
cust10 | cust10@cust.com |
cust1 | cust1@cust.com |
cust2 | cust2@cust.com |
【问题】返回购买 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';
# SQL35 返回每个顾客不同订单的总金额
我们需要一个顾客 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 以便返回每个顾客的订单总数,将结果按金额从大到小排序。
# 写法 1:子查询
SELECT
o.cust_id cust_id,
tb.total_ordered total_ordered
FROM (
SELECT order_num, SUM(item_price * quantity) 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;
# SQL36 从 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,会报错)
# 连接表 join...on
Join 是“连接”的意思,顾名思义,SQL JOIN 子句用于将两个或者多个表联合起来进行查询。
连接表时需要在每个表中选择一个字段,并对这些字段的值进行比较,值相同的两条记录将合并为一条。连接表的本质就是将不同表的记录合并起来,形成一张新表。当然,这张新表只是临时的,它仅存在于本次查询期间。
使用 SQL JOIN 连接两个表的基本语法如下:
SELECT table1.column1, table2.column2...
FROM table1
JOIN table2
ON table1.common_column1 = table2.common_column2;
table1.common_column1 = table2.common_column2
是连接条件,只有满足此条件的记录才会合并为一行。您可以使用多个运算符来连接表,例如 =、>、<、<>、<=、>=、!=、BETWEEN、LIKE 或者 NOT,但是最常见的是使用 =。
当两个表中有同名的字段时,为了帮助数据库引擎区分是哪个表的字段,在书写同名字段名时需要加上表名。当然,如果书写的字段名在两个表中是唯一的,也可以不使用以上格式,只写字段名即可。
另外,如果两张表的关联字段名相同,也可以使用 USING
子句来代替 ON
,举个例子:
# join....on
select c.cust_name, o.order_num
from Customers c
inner join Orders o
on c.cust_id = o.cust_id
order by c.cust_name;
# 如果两张表的关联字段名相同,也可以使用USING子句:join....using()
select c.cust_name, o.order_num
from Customers c
inner join Orders o
using(cust_id)
order by c.cust_name;
ON 和 WHERE 的区别:
- 连接表时,SQL 会根据连接条件生成一张新的临时表。ON 就是连接条件,它决定临时表的生成。
- WHERE 是在临时表生成以后,再对临时表中的数据进行过滤,生成最终的结果集,这个时候已经没有 JOIN-ON 了。
所以总结来说就是:SQL 先根据 ON 生成一张临时表,然后再根据 WHERE 对临时表进行筛选。
不同的连接类型:
SQL 允许在 JOIN 左边加上一些修饰性的关键词,从而形成不同类型的连接,如下表所示:
连接类型 | 说明 |
---|---|
INNER JOIN 内连接 | (默认连接方式)只有当两个表都存在满足条件的记录时才会返回行。 |
LEFT JOIN / LEFT OUTER JOIN 左(外)连接 | 返回左表中的所有行,即使右表中没有满足条件的行也是如此。 |
RIGHT JOIN / RIGHT OUTER JOIN 右(外)连接 | 返回右表中的所有行,即使左表中没有满足条件的行也是如此。 |
FULL JOIN / FULL OUTER JOIN 全(外)连接 | 只要其中有一个表存在满足条件的记录,就返回行。 |
SELF JOIN | 将一个表连接到自身,就像该表是两个表一样。为了区分两个表,在 SQL 语句中需要至少重命名一个表。 |
CROSS JOIN | 交叉连接,从两个或者多个连接表中返回记录集的笛卡尔积。 |
如果不加任何修饰词,只写 JOIN,那么默认为 INNER JOIIN
对于 inner join 来说,还有一种隐式的写法,称为 “隐式内连接”,也就是没有 inner join 关键字,使用 where 语句实现内连接的功能
# 隐式内连接
select c.cust_name, o.order_num
from Customers c, Orders o
where c.cust_id = o.cust_id
order by c.cust_name;
# 显式内连接
select c.cust_name, o.order_num
from Customers c inner join Orders o
using(cust_id)
order by c.cust_name;
# SQL37 返回顾客名称和相关订单号
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;
# 显式内连接
select c.cust_name, o.order_num
from Customers c inner join Orders o
using(cust_id)
order by c.cust_name;
# SQL38 返回顾客名称和相关订单号以及每个订单的总价
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(quantity * item_price) as OrderTotal
from Customers c, Orders o, OrderItems oi
where c.cust_id = o.cust_id
and o.order_num = oi.order_num
group by c.cust_name, o.order_num
order by c.cust_name, o.order_num;
注意,可能有小伙伴会这样写:
select c.cust_name, o.order_num, sum(quantity * item_price) as OrderTotal
from Customers c, Orders o, OrderItems oi
where c.cust_id = o.cust_id
and o.order_num = oi.order_num
group by c.cust_name
order by c.cust_name, o.order_num;
这是错误的!只对 cust_name 进行聚类确实符合题意,但是不符合 group by 的语法。
select 语句中,如果没有 group by 语句,那么 cust_name、order_num 会返回若干个值,而 sum(quantity * item_price) 只返回一个值,通过 group by cust_name 可以让 cust_name 和 sum(quantity * item_price) 一一对应起来,或者说聚类,所以同样的,也要对 order_num 进行聚类。
一句话,select 中的字段要么都聚类,要么都不聚类
# SQL39 确定哪些订单购买了 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),按订购日期对结果进行升序排序。
提示:这一次使用连接和简单的等连接语法。
# 写法 1:子查询
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:连接表 inner join
select cust_id, order_date
from Orders o inner join (
select order_num
from OrderItems
where prod_id = 'BR01'
) tb on o.order_num = tb.order_num
order by order_date;
# 写法 3:写法 2 的简化版
select cust_id, order_date
from Orders
inner join OrderItems using(order_num)
where OrderItems.prod_id = 'BR01'
order by order_date;
# SQL40 返回购买 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
cust_id | cust_email |
---|---|
cust10 | cust10@cust.com |
cust1 | cust1@cust.com |
cust2 | cust2@cust.com |
【问题】返回购买 prod_id 为BR01 的产品的所有顾客的电子邮件(Customers 表中的 cust_email),结果无需排序。
提示:涉及到 SELECT 语句,最内层的从 OrderItems 表返回 order_num,中间的从 Customers 表返回 cust_id,但是必须使用 INNER JOIN 语法。
select cust_email
from Customers
inner join Orders using(cust_id)
inner join OrderItems using(order_num)
where OrderItems.prod_id = 'BR01';
# SQL41 确定最佳顾客的另一种方式(二)
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;
# 创建高级连接
# SQL42 检索每个顾客的名称和所有的订单号(一)
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;
# SQL43 检索每个顾客的名称和所有的订单号(二)
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;
# SQL44 返回产品名称和与之相关的订单号
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 |
【问题】
使用外连接(left join、 right join、full join)联结 Products 表和 OrderItems 表,返回产品名称(prod_name)和与之相关的订单号(order_num)的列表,并按照产品名称升序排序。
select prod_name, order_num
from Products
left join OrderItems
using(prod_id)
order by prod_name;
# SQL45 返回产品名称和每一项产品的总订单数
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
from Products
left join OrderItems
using(prod_id)
group by prod_name
order by prod_name;
# SQL46 列出供应商及其可供产品的数量
有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 vend_id, count(prod_id) as prod_id
from Vendors
left join Products
using(vend_id)
group by vend_id
order by vend_id;
# 组合查询 union
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。
请注意,UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同。
SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;
默认地,UNION
操作符选取不同的值。如果允许重复的值,请使用 UNION ALL
。
SELECT column_name(s) FROM table1
UNION ALL
SELECT column_name(s) FROM table2;
UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名。
# SQL47 将两个 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%';
# SQL48 将两个 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 语句。
要求只用一条 select 语句,那就用 or
不用 union
了
select prod_id, quantity
from OrderItems
where quantity = 100 or prod_id like 'BNBG%';
# SQL49 组合 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;
# SQL50 纠错4
表Customers含有字段cust_name顾客名、cust_contact顾客联系方式、cust_state顾客州、cust_email顾客email
cust_name | cust_contact | cust_state | cust_email |
---|---|---|---|
cust10 | 8695192 | MI | cust10@cust.com |
cust1 | 8695193 | MI | cust1@cust.com |
cust2 | 8695194 | IL | cust2@cust.com |
【问题】修正下面错误的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;
修正后:
使用 union 组合查询时,只能使用一条 order by 字句,他必须位于最后一条 select 语句之后
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;
或者直接用 or 来做:
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state = 'MI' or cust_state = 'IL'
ORDER BY cust_name;
🎁 公众号

各位小伙伴大家好呀,叫我小牛肉就行,目前在读东南大学硕士,上方扫码关注公众号「飞天小牛肉」,与你分享我的成长历程与技术感悟~