优秀是一种习惯!!!
Solo  当前访客:0 开始使用

AmethystFOB

记录精彩人生

SQL刷题Day01~Day06

2023-11-28 13:41:14 amethystfob
0  评论    0  浏览

SQL刷题

SQL入门题:

Day01选择:

1、1757. 可回收且低脂的产品

题目:
表:Products

Column NameType
product_idint
low_fatsenum
recyclableenum

product_id 是这个表的主键。
low_fats 是枚举类型,取值为以下两种 ('Y', 'N'),其中 'Y' 表示该产品是低脂产品,'N' 表示不是低脂产品。
recyclable 是枚举类型,取值为以下两种 ('Y', 'N'),其中 'Y' 表示该产品可回收,而 'N' 表示不可回收。写出 SQL 语句,查找既是低脂又是可回收的产品编号。
返回结果 无顺序要求 。查询结果格式如下例所示:
Products表:

product_idlow_fatsrecyclable
0YN
1YY
2NY
3YY
4NN

Result表:

product_id
1
3

只有产品 id 为 1 和 3 的产品,既是低脂又是可回收的产品。

解答:关于对称逻辑
方法一:

select product_id
from products
where low_fats = 'Y' and recyclable = 'Y';

方法二:

select product_id
from products
where !(low_fats = 'N' or recyclable = 'N');

方法三:

select product_id
from products
where (low_fats,recyclable) = ('Y','Y');

方法四: <> 为不等于

select product_id
from products
where (low_fats,recyclable)<>('Y','N')
and (low_fats,recyclable)<>('N','Y')
and (low_fats,recyclable)<>('N','N');

方法五: * 号的使用 内连接?

select product_id
from products
where (low_fats = 'Y') * (recyclable = 'Y');

方法六:

select product
from products
where !((low_fats = 'Y')*(recyclable = 'N') or (low_fats = 'N') * (recyclable = 'Y') or (low_fats = 'N') * (recyclable = 'N'));

方法七: 拼接函数 concat() 的使用

select product_id
from products
where concat(low_fats,recyclable) = 'YY';

方法八:

select product_id
from products
where !(concat(low_fats,recyclable) = 'YN' or concat(low_fats,recyclable) = 'NY' or concat(low_fats,recyclable) = 'NN');

方法九: with as 子查询用法、rank() 结合over()子句用法、ifnull()、max()

with A as (select *, rank() over (order by low_fats desc, recyclable desc) sort from products)
select product_id
from A
where sort > ifnull((select max(sort) from A where (low_fats,recyclable)<>('Y','Y')),0)

方法十:

with A as (select *, rank() over (order by low_fats,recyclable) sort from products)
select product_id
from A
where sort < ifnull((select min(sort) from A where (low_fats,recyclable)<>('Y','Y')),(select max(sort)+1 from A))

2、595. 大的国家

题目:

world表:

column nametype
namevarchar
continentvarchar
areaint
populationint
gdpint

name 是这张表的主键。
这张表的每一行提供:国家名称、所属大陆、面积、人口和 GDP 值。如果一个国家满足下述两个条件之一,则认为该国是大国
面积至少为 300 平方公里(即,3000000 km2),或者人口至少为 2500 万(即 25000000)
编写一个 SQL 查询以报告大国的国家名称、人口和面积。按任意顺序返回结果表。

解答:

方法一:

select name, population, area
from world
where area >= 3000000 or population >= 25000000;

方法二:

select name, population, area
from world
where area >= 3000000
union
select name, population, area
from world
where population >= 25000000;

方法二比一运行速度更快。
使用 or 会使索引会失效,在数据量较大的时候查找效率较低,通常建议使用 union 代替 or。

or和union的区别:对于单列来说,用or是没有任何问题的,但是or涉及到多个列的时候,每次select只能选取一个index,如果选择了area,population就需要进行table-scan,即全部扫描一遍,但是使用union就可以解决这个问题,分别使用area和population上面的index进行查询。 但是这里还会有一个问题就是,UNION会对结果进行排序去重,可能会降低一些performance(这有可能是方法一比方法二快的原因),所以最佳的选择应该是两种方法都进行尝试比较。

3、584. 寻找用户推荐人

题目:给定表 customer,里面保存了所有客户信息和他们的推荐人。

idnamereferee_id
1WillNULL
2JaneNULL
3Alex2
4BillNULL
5Zack1
6Mark2

写一个查询语句,返回一个客户列表,列表中客户的推荐人的编号都 不是 2。
对于上面的示例数据,结果为:

name
Will
Jane
Bill
Zack

解答:

方法一

select c1.name
from customer c1
left join customer c2 on c1.id = c2.id
where (c1.referee_id <> 2) or (c2.referee_id is null);

方法二

select name from customer where referee_id != 2 or referee_id is null;

4、183. 从不订购的客户

题目:某网站包含两个表,Customers表和 Orders 表。编写一个 SQL 查询,找出所有从不订购任何东西的客户。
Customers表:

IdName
1Joe
2Henry
3Sam
4Max

Orders表:

IdCustomerId
13
21

例如给定上述表格,你的查询应返回:

Customers
Henry
Max

解答:

方法一: 左连接

select name as Customers
from customers cu
left join orders od on cu.id = od.customerid
where customerid is null;

方法二: not in

select customers.name as 'Customers'
from customers
where customers.id not in (
	select customerId from orders
);

Day02排序&修改:

5、1873. 计算特殊奖金

题目: 表: Employees

列名类型
employee_idint
namevarchar
salaryint

employee_id 是这个表的主键。此表的每一行给出了雇员id ,名字和薪水。

写出一个SQL 查询语句,计算每个雇员的奖金。如果一个雇员的id是奇数并且他的名字不是以'M'开头,那么他的奖金是他工资的100%,否则奖金为0。

Return the result table ordered by employee_id.
返回的结果集请按照employee_id排序。

查询结果格式如下面的例子所示。
输入:Employees表:

employee_idnamesalary
2Meir3000
3Michael3800
7Addilyn7400
8Juan6100
9Kannon7700

输出:

employee_idbonus
20
30
77400
80
97700

解释:
因为雇员id是偶数,所以雇员id 是2和8的两个雇员得到的奖金是0。
雇员id为3的因为他的名字以'M'开头,所以,奖金是0。
其他的雇员得到了百分之百的奖金。

解答:

方法一: if

select employee_id, if(employee_id % 2 = 1 and left(name,1)!='M', salary, 0) as bonus
from employees

方法二: case when then end

select employee_id, 
	case 
		when employee_id % 2 = 1 and left(name,1)!='M' then salary
		else 0
	end bonus
from employees

方法三: mod()函数、not like、order by

select employee_id,
	case when mod(employee_id, 2) = 1 and name not like 'M%' then salary
	else 0
	end as bonus
	from employees order by employee_id;

注意:Case函数只返回第一个符合条件的值,剩下的Case部分将会被自动忽略,eg:

CASE sex
WHEN '1' THEN '男'
WHEN '2' THEN '女'
ELSE '其他'

6、627. 变更性别

题目: salary表:

column nametype
idint
namevarchar
sexenum
salaryint

id 是这个表的主键。sex 这一列的值是 ENUM 类型,只能从 ('m', 'f') 中取。本表包含公司雇员的信息。

编写一个 SQL 查询来交换所有的 'f' 和 'm' (即,将所有 'f' 变为 'm' ,反之亦然),仅使用 单个 update 语句 ,且不产生中间临时表。

注意,必须仅使用一条 update 语句,且 不能 使用 select 语句。

查询结果如下例所示。

示例1:

输入:salary表:

idnamesexsalary
1Am2500
2Bf1500
3Cm5500
4Df500

输出:

idnamesexsalary
1Af2500
2Bm1500
3Cf5500
4Dm500

解释:

(1, A) 和 (3, C) 从 'm' 变为 'f' 。
(2, B) 和 (4, D) 从 'f' 变为 'm' 。

解答:

方法一: case when then else end

update salary
set sex = case sex
			when 'm' then 'f'
			else 'm'
			end;

方法二: if('','','')

update salary set sex = if (sex = 'm','f','m');

7、196. 删除重复的电子邮箱

题目: 表person

column nametype
idint
emailvarchar

id是该表的主键列。该表的每一行包含一封电子邮件。电子邮件将不包含大写字母。

编写一个SQL查询来 删除 所有重复的电子邮件,只保留一个id最小的唯一电子邮件。

任意顺序 返回结果表。查询结果格式如下所示。

示例1:
输入:person表:

idemail
1john@example.com
2bob@example.com
3john@example.com

输出:

idemail
1john@example.com
2bob@example.com

解释: john@example.com重复两次。我们保留最小的Id = 1。

解答:

方法一:

delete p1 from person p1, person p2
where p1.email = p2.email and p1.id > p2.id;

方法一缺点:若数据量大,笛卡尔积会组合出非常大的零时表,实际中不应使用。

方法二:

delete a from person as a inner join person as b
where a.email = b.email and a.id > b.id;

思路同方法一,就换了个皮~~

方法三: 分析函数中的分组/排序/窗口(partition by、order by、rows)

delete from person
where id not in (
	select keep_id from (
    	select min(id) over(partition by email) as keep_id
        from person
    ) t1
);

方法四: 开窗函数解法

delete from person
where id in (
	select id from (
    	select id, row_number() over(partition by email order by id) rn
        from person
    )t1
    where rn > 1
)

对同一邮箱的id排序,删除id>1的

方法五: not exists 比 not in快?

delete from person as a
where not exists(
	select need.id from (
    	select min(id) as id from person group by email
    )as need
    where need.id = a.id
);

方法六: not exists 比 not in快?

delete from person
where id not in (
	select need.id from (
    	select min(id) as id from person group by email
    )as need
)

Day03字符串处理函数/正则:

8、1484. 按日期分组销售产品

题目: 表activities:

列名类型
sell_datedate
productvarchar

此表没有主键,它可能包含重复项。
此表的每一行都包含产品名称和在市场上销售的日期。

编写一个 SQL 查询来查找每个日期、销售的不同产品的数量及其名称。
每个日期的销售产品名称应按词典序排列。
返回按 sell_date 排序的结果表。
查询结果格式如下例所示。

示例1:

输入:activities表:

sell_dateproduct
2020-05-30headphone
2020-06-01pencil
2020-06-02mask
2020-05-30basketball
2020-06-30bible
2020-06-30mask
2020-05-30t-shirt

输出:

sell_datenum_soldproducts
2020-05-303basketball,headphone,t-shirt
2020-06-012bible,pencil
2020-06-021mask

解释:
对于2020-05-30,出售的物品是 (Headphone, Basketball, T-shirt),按词典序排列,并用逗号 ',' 分隔。
对于2020-06-01,出售的物品是 (Pencil, Bible),按词典序排列,并用逗号分隔。
对于2020-06-02,出售的物品是 (Mask),只需返回该物品名。

解答:

方法一:

select sell_date, 
		count(distinct product) num_sold, 
		group_concat(distinct product) products
	from activities
	group by sell_date
	order by sell_date;

方法二:

select sell_date as 'sell_date',
	count(distinct product) as 'num_sold',
	group_concat(distinct product
                order by product ASC
                separator ',')
                as 'products'
     from activities
     group by sell_date
     order by sell_date;

9、1667. 修复表中的名字

题目: 表user

column nametype
user_idint
namevarchar

user_id 是该表的主键。
该表包含用户的 ID 和名字。名字仅由小写和大写字符组成。

编写一个 SQL 查询来修复名字,使得只有第一个字符是大写的,其余都是小写的。

返回按user_id排序的结果表。查询结果格式示例如下。

示例1:输入:users table:

user_idname
1alice
2bob

输出:

user_idname
1alice
2bob

解答:

方法一:

select 
user_id, concat(ucase(left(name,1)), lcase(substring(name,2))) name
from users
order by user_id;

方法二:

select user_id, concat(upper(left(name,1)),lower(right(name,length(name)-1))) name
from users
order by user_id; 

10、1527. 患某种疾病的患者

题目: 患者信息表:patients

column nametype
patient_idint
patient_namevarchar
conditionsvarchar

patient_id (患者 ID)是该表的主键。
'conditions' (疾病)包含 0 个或以上的疾病代码,以空格分隔。
这个表包含医院中患者的信息。

写一条 SQL 语句,查询患有 I 类糖尿病的患者 ID (patient_id)、患者姓名(patient_name)以及其患有的所有疾病代码(conditions)。I 类糖尿病的代码总是包含前缀 DIAB1 。按任意顺序返回结果表。查询结果格式如下示例所示。

示例1:输入:patients表:

patient_idpatient_nameconditions
1danielyfev cough
2alice
3bobdiab100 myop
4georgeacne diab100
5alaindiab201

输出:

patient_idpatient_nameconditions
3bobdiab100 myop
4georgeacne diab100

解释:Bob 和 George 都患有代码以 DIAB1 开头的疾病。

解答:

方法一: like()

select * from patients where conditions like('% DIAB1%') or conditions like('DIAB1%');

方法二: RegExp binary

select * from patients
where conditions RegExp binary '^DIAB1|\\sDIAB1';

Day04组合查询 & 指定选取:

11、1795. 每个产品在不同商店的价格

题目: 表products

column nametype
product_idint
store1int
store2int
store3int

这张表的主键是product_id(产品Id)。每行存储了这一产品在不同商店store1, store2, store3的价格。如果这一产品在商店里没有出售,则值将为null。

请你重构 Products 表,查询每个产品在不同商店的价格,使得输出的格式变为(product_id, store, price) 。如果这一产品在商店里没有出售,则不输出这一行。输出结果表中的 顺序不作要求 。查询输出格式请参考下面示例。

示例 1:输入:Products table:

product_idstore1store2store3
095100105
170null80

输出:

product_idstoreprice
0store195
0store2100
0store3105
1store170
1store380

解释:
产品0在store1,store2,store3的价格分别为95,100,105。
产品1在store1,store3的价格分别为70,80。在store2无法买到。

解答: 行转列、列转行常用:1、case when 2、union all

方法一:

select product_id,'store1' store, store1 proice from products
	where store1 is not null
	union all
select product_id,'store2' store, store2 price from products
	where store2 is not null
	union all
select product_id,'store3' store, store3 price from products
	where store3 is not null

总结方法 eg:

行表:student1:

namesubjectscore
小明语文96
小明数学98
小明英语95
大花语文92
大花数学96
大花英语98

上下表互换

列表:student2:

姓名语文数学英语
小明969895
大花929698

【行转列——max / sum + case when + group by】

select name,
	max(case when subject='语文' then score else 0 end) as '语文',
	max(case when subject='数学' then score else 0 end) as '数学',
	max(case when subject='英语' then score else 0 end) as '英语'
from student1
group by name

【列转行——max + union + group by】

select name,'语文' as subject,max("语文") as score
	from student2 group by name
		union
select name,'数学' as subject,max('数学') as score
	from student2 group by name
		union
select name,'英语' as subject,max('英语') as score
	from student2 group by name

Union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;

Union All:对两个结果集进行并集操作,包括重复行,不进行排序;

12、1965. 丢失信息的雇员

题目: 表:employees

column nametype
employee_idint
namevarchar

empoyee_id 是这个表的主键。每一行表示雇员的id和他的姓名。

表salaries

column nametype
employee_idint
salaryint

employee_id 是这个表的主键。每一行表示雇员的id和他的薪水。

写出一个查询语句,找到所有 丢失信息 的雇员id。当满足下面一个条件时,就被认为是雇员的信息丢失:

  • 雇员的 姓名 丢失了,或者
  • 雇员的 薪水信息 丢失了,或者
  • 返回这些雇员的id employee_id , 从小到大排序 。查询结果格式如下面的例子所示。

示例1:输入:employees table:

employee_idname
2crew
4haven
5kristian

salaries table:

employee_idsalary
576071
122517
463539

输出:

employee_id
1
2

解释:
雇员1,2,4,5 都工作在这个公司。
1号雇员的姓名丢失了。
2号雇员的薪水信息丢失了。

解答:

方法一:

select employees.employee_id from employees left join salaries on employees.employee_id = salaries.employee_id where salary is null
union
select salaries.employee_id from employees right join salaries on salaries.employee_id = employees.employee_id where name is null
order by employee_id

方法二: 思路:雇员的姓名丢失了或者雇员的薪水信息丢失,都会导致employee_id 在 employees 和salaries 的并集表里面仅出现一次。

select employee_id
	from (
    	select employee_id from employees
        union all
        select employee_id from salaries
    )as t
    group by employee_id
    having count(employee_id) = 1
    order by employee_id;

方法三: 全连接取employee_id是null

select case when a.employee_id is null then b.employee_id else a.employee_id end as employee_id
from employees  a full join salaries b on a.employee_id = b.employee_id where a.employee_id is null or b.employee_id is null
order by employee_id

13、608. 树节点(中等)

题目: 给定一个表 tree,id 是树节点的编号, p_id 是它父节点的 id 。

idp_id
1null
21
31
42
52

树中每个节点属于以下三种类型之一:

  • 叶子:如果这个节点没有任何孩子节点。
  • 根:如果这个节点是整棵树的根,即没有父节点。
  • 内部节点:如果这个节点既不是叶子节点也不是根节点。

写一个查询语句,输出所有节点的编号和节点的类型,并将结果按照节点编号排序。上面样例的结果为:

idtype
1root
2inner
3leaf
4leaf
5leaf

解释

节点 '1' 是根节点,因为它的父节点是 NULL ,同时它有孩子节点 '2' 和 '3' 。
节点 '2' 是内部节点,因为它有父节点 '1' ,也有孩子节点 '4' 和 '5' 。
节点 '3', '4' 和 '5' 都是叶子节点,因为它们都有父节点同时没有孩子节点。
样例中树的形态如下:

	  			  1
				/   \
              2       3
            /   \
          4       5

注意

如果树中只有一个节点,只需要输出它的根属性。

解答:

方法一:

select id, 'root' as type from tree where p_id is null
uniom
select id, 'leaf' as type from tree where id not in(
	select distinct p_id from tree where p_id is not null
)and p_id is not null
union
select id, 'inner' as type from tree where id in(
	select distinct p_id from tree where p_id is not null
)and p_id is not null
order by id;

方法二:

select id, case
	when p_id is null then 'Root'
	when id in (select distinct p_id from tree) then 'Inner'
	else 'Leaf'
	end as type
from tree
order by id

但是换为 not in 就错了

14、176. 第二高的薪水(中等)

题目: employee表

column nametype
idint
salaryint

id是这个表的主键,表的每一行包含员工的工资信息。编写一个 SQL 查询,获取并返回 Employee表中第二高的薪水 。如果不存在第二高的薪水,查询应该返回 null 。查询结果如下例所示。

示例1:输入:employee表:

idsalary
1100
2200
3300

输出:

seacondHighestSalary
200

示例2:

输入:employee表:

idsalary
1100

输出:

secondHighestSalary
null

解答:

方法一: limit offset 限制偏移量

select(
	select distinct salary from employee order by salary desc limit 1 offset 1
) as SecondHighestSalary

当select语句中包含distinct时,无论遇到多少个空值,结果中只返回一个null

方法二: ifnull

select ifnull(
	(select distinct salary from employee order by salary desc limit 1 offset 1),null
)as SecondHighestSalary

Day05+Day06 合并

cross join 也就是交叉连接,直接两个表求笛卡尔积,而在求笛卡尔积的基础上,又可以分为内连接 inner join/join 和外 outer join,内外连接都是先求笛卡尔积,然后在此基础上满足一定的条件,内连接是满足条件的那部分,而外连接则要加上不满足条件的那部分。 总之,内连接和交叉连接其实可以互换,改下限制条件而已。

15、1581. 进店却未进行过交易的顾客

题目: 表visits:

column nametype
visit_idint
customer_idint

visit_id 是该表的主键。该表包含有关光临过购物中心的顾客的信息。

表:transactions

column nametype
transaction_idint
visit_idint
amountint

transaction_id 是此表的主键。此表包含 visit_id 期间进行的交易的信息。

有一些顾客可能光顾了购物中心但没有进行交易。请你编写一个 SQL 查询,来查找这些顾客的 ID ,以及他们只光顾不交易的次数。返回以 任何顺序 排序的结果表。查询结果格式如下例所示。

示例:输入:visits

visit_idcustomer_id
123
29
430
554
696
754
854

transactions

transaction_idvisit_idamount
25310
35300
95200
121910
132970

输出:

customer_idcount_no_trans
542
301
961

解释:
ID = 23 的顾客曾经逛过一次购物中心,并在 ID = 12 的访问期间进行了一笔交易。
ID = 9 的顾客曾经逛过一次购物中心,并在 ID = 13 的访问期间进行了一笔交易。
ID = 30 的顾客曾经去过购物中心,并且没有进行任何交易。
ID = 54 的顾客三度造访了购物中心。在 2 次访问中,他们没有进行任何交易,在 1 次访问中,他们进行了 3 次交易。
ID = 96 的顾客曾经去过购物中心,并且没有进行任何交易。
如我们所见,ID 为 30 和 96 的顾客一次没有进行任何交易就去了购物中心。顾客 54 也两次访问了购物中心并且没有进行任何交易。

解答:

方法一:

select visits.customer_id, count(transactions.amount is null) as count_no_trans
    from visits 
    left join transactions on visits.visit_id = transactions.visit_id
    where amount is null
    group by visits.customer_id
    order by count_no_trans DESC;

方法二:

select 
	customer_id,count(customer_id) count_no_trans
from
	visits v
left join 
	transactions t on v.visit_id = t.visit_id
where amount is null
group by customer_id;

16、607. 销售员

题目: 表:salesperson

column nametype
sales_idint
namevarchar
salaryint
commission_rateint
hire_datedate

Sales_id是该表的主键列。该表的每一行都显示了销售人员的姓名和ID,以及他们的工资、佣金率和雇佣日期。

表:company

column nametype
com_idint
namevarchar
cityvarchar

Com_id是该表的主键列。该表的每一行都表示公司的名称和ID,以及公司所在的城市。

表:orders

column nametype
order_idint
order_datedate
com_idint
sales_idint
amountint

Order_id是该表的主键列。com_id是Company表中com_id的外键。sales_id是来自销售员表com_id的外键。该表的每一行包含一个订单的信息。这包括公司的ID、销售人员的ID、订单日期和支付的金额。

编写一个SQL查询,报告没有任何与名为 “RED” 的公司相关的订单的所有销售人员的姓名。以 任意顺序 返回结果表。

解答:

方法一:

select name from salesperson where name not in(
    select salesperson.name 
    from salesperson 
    left join orders on salesperson.sales_id = orders.sales_id
    left join company on company.com_id = orders.com_id
    where company.name = 'RED'
);

方法二:

select s.name from salesperson s
where s.sales_id not in(
	select o.sales_id from orders o
    left join company c on o.com_id = c.com_id
    where c.name = 'RED'
);

17、197. 上升的温度

a.日期比b.日期大一天

可以使用“datediff(a.日期, b.日期) = 1”或者“timestampdiff(day, a.日期, b.日期) = -1”

题目: 表weather

column nametype
idint
recordDatedate
temperatureint

id 是这个表的主键。该表包含特定日期的温度信息。编写一个 SQL 查询,来查找与之前(昨天的)日期相比温度更高的所有日期的 id 。返回结果 不要求顺序 。

解答:

select w1.id
	from weather w1 cross join weather w2 on datediff(w1.recorddate, w2.recorddate) = 1
	and w1.temperature > w2.temperature;

标题:SQL刷题Day01~Day06
作者:amethystfob
地址:https://newmoon.top/articles/2023/11/27/1701065857822.html

欢迎各路大侠指点留痕:
, , ,
TOP