连接join练习解答

count(null)对空值的处理

聚合函数对空值结果未null,统计null时会自动去除,直接计算count(null)为0

交易了却未消费的交易

通过金钱amount筛选未交易顾客存在漏洞,有的顾客交易了存在交易id但未消费,transaction有但amount为0,这时候应该根据交易id统计

on和where区别

日期函数计算天数、增加日期

连接

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

查找只光顾了购物中心但没有进行交易的顾客 ID ,以及他们只光顾不交易的次数。

交易表、顾客访问表
查询所有的访问顾客,即使没有交易;所以顾客访问表要全部记录;
采取左连接,右表交易表中没有的直接为null,
过滤出这些null的顾客,以顾客id分组,统计每位顾客的访问次数

以左连接的方式连接两张表,
customer_id,Visits.visit_id,amount

    select *
    from Visits left join Transactions
    on Visits.visit_id = Transactions.visit_id

在这里插入图片描述
过滤出这些null的记录,以顾客分组,统计每个顾客的null多少;

select *-- customer_id,count(Visits.visit_id) as count_no_trans
from Visits left join Transactions
on Visits.visit_id = Transactions.visit_id
where amount is null
group by customer_id 

在这里插入图片描述
关于count对null的处理:
这里count(amount)或者count(transaction_id)等本身为空的内容,也就是count(null),结果恒为0;这里应该count(visit_id)或count(customer_id)等非null的值;

大部分集合函数在进行计算时都去除NULL值,count(字段)也不计算null值;
但是count(1)与count(*)得到的结果一致,包含null值;
count(null)结果恒为0;

如何处理null值,或者进行合法的代替:

IF(expr1,expr2,expr3),如果expr1的值为true,则返回expr2的值,如果expr1的值为false,则返回expr3的值。
IFNULL(expr1,expr2) 函数用于判断第一个表达式是否为 NULL,如果为 NULL ,则返回第二个参数的值,如果不为 NULL, 则返回第一个参数的值
isnull(expr) 的用法
如expr 为null,那么isnull() 的返回值为 1,否则返回值为 0。

这里用amount is null进行筛选判断出错
原因:题目统计进店但未交易的顾客,有的顾客可能交易了但没花钱,所以不能用amount消费金钱来判断是否顾客交易;
或者说,有的顾客进店光顾了多次,有几次参观交易了金钱,但有的时候仅参观未交钱,如下面的顾客,参观了3次,有两次参观消费了,但有一次参观未消费;这样的顾客不能算进店却未交易的顾客;所以用amount is null判断不准确。用交易id或交易表中的visit_id都可以。
在这里插入图片描述
最后:
在这里插入图片描述

2. 使用唯一标识码替换员工身份id

关于on和where区别

ON 和 WHERE 在 SQL 中都用于设置条件,但它们在连接(JOIN)操作中的作用是不同的。
ON:在 JOIN 操作中,ON 关键字用于设置连接条件。例如,在你的查询中,ON Employees.id=EmployeeUNI.id 是连接 Employees 表和 EmployeeUNI 表的条件,即只有当两个表中的 id 相等时,才会将这两个表的行连接在一起。
WHERE:WHERE 关键字用于过滤结果集,即只返回满足特定条件的行。如果你在查询中使用 WHERE Employees.id=EmployeeUNI.id,那么只有当 Employees 表和 EmployeeUNI 表中的 id 相等时,才会在结果集中返回这些行。
总的来说,ON 关键字定义了如何连接两个表,而 WHERE 关键字定义了如何过滤这些连接后的结果。在某些情况下,使用 ON 和 WHERE 可能会得到相同的结果,但在其他情况下,结果可能会有所不同,特别是在执行外连接(如 LEFT JOIN 或 RIGHT JOIN)时。
内连接(INNER JOIN)只返回两个表中都有匹配的行。
左连接(LEFT JOIN)返回左表(这里是 Employees 表)的所有行,如果右表(这里是 EmployeeUNI 表)中没有匹配的行,则结果集中的对应列将为 NULL。
WHERE 子句返回满足特定条件的行,这里的条件是 Employees.id 等于 EmployeeUNI.id。

获取每位用户的 唯一标识码(unique ID );如果某位员工没有唯一标识码,使用 null 填充即可。这里没有用到case when| if,直接left join就处理了不匹配的null。

3. 上升的温度

找出今天比昨天温度高的id

  • 思路:表连接起来,连接类型,笛卡尔积、内连接都可以,连接条件on是今天大于昨天1个day,过滤条件where是今天温度大于昨天;

  • 连接类型: 表自连接,内连接inner join、join、笛卡尔积cross join、自然连接natural join;

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

如何找到 “昨天”(前一天),这里为大家介绍两个时间计算的函数:

datediff(日期1, 日期2):
得到的结果是日期1与日期2相差的天数。日期1-日期2

timestampdiff(时间类型, 日期1, 日期2)
日期2-日期1;在“时间类型”的参数位置,通过添加“day”, “hour”, “second”等关键词,来规定计算天数差、小时数差、还是分钟数差。

我这里用的date_add(日期,interval 1 day),日期+1。左表日期=右边日期+1,左表日期-右表日期=1;这表示左表是今天,右表是昨天;如果左表日期=右边日期-1,表示右表大,右表是今天,那么后面比较温度、取值的时候,记得左表温度应该<右表,取右表的id

	select Id 
	from weather a join weather b
	on a.recordDate = date_add(b.recordDate,interval 1 day)
	where a.temperature > b.temperature

	select a.id
	from weather a cross join weather b
	on datediff(a.recordDate,b.recordDate)=1
	where a.temperature > b.temperature

注意:针对两个表共有的列,一定要用表.列指定
Column ‘Id’ in field list is ambiguous 直接写id列名具有二义性,是不确定的

4.每台机器的进程平均运行时间

解法一、avg计算平均

表自连接,通过机器id和进程id连接两张表,类似于笛卡尔积,每组(机器id,进程id)都会对应多组(机器id,进程id,…),1对n,再n对n;但每组(机器id,进程id)都只有一组(机器id,进程id,start,end)

根据(机器id,进程id)自连接,查询连接后的所有行(笛卡尔积)
select *
from  Activity a1 inner JOIN Activity a2
WHERE a1.machine_id = a2.machine_id
      AND a1.process_id = a2.process_id 

找出每组(机器id,进程id)唯一的(机器id,进程id,start,end)
select *
from  Activity a1 inner JOIN Activity a2
WHERE a1.machine_id = a2.machine_id
      AND a1.process_id = a2.process_id 
      AND a1.activity_type = 'start'
      AND a2.activity_type = 'end'

在这里插入图片描述

过滤出机器id,计算出每组(机器id,进程id,start,end)的进程时间
select a1.machine_id , (a2.timestamp -a1.timestamp) processing_time 
from  Activity a1 inner JOIN Activity a2
WHERE a1.machine_id = a2.machine_id
      AND a1.process_id = a2.process_id 
      AND a1.activity_type = 'start'
      AND a2.activity_type = 'end'

在这里插入图片描述

以机器id分组,计算出每组机器的平均值
select a1.machine_id ,round(avg(a2.timestamp -a1.timestamp ),3)processing_time 
from  Activity a1 inner JOIN Activity a2
WHERE a1.machine_id = a2.machine_id
      AND a1.process_id = a2.process_id 
      AND a1.activity_type = 'start'
      AND a2.activity_type = 'end'
group by a1.machine_id

在这里插入图片描述

解法二、sum/count计算平均

针对这张表
在这里插入图片描述
以机器id分组,
计算和:进程时间=结束-开始。如果type是start,变为负值,-start;如果type是end,不变,
统计每个机器id的所有进程时间,=-start+end-start+end,即结束-开始之和;
统计进程数:直接count(distinct process_id),统计每个机器有几个不同的进程Id;

SELECT machine_id,
    round(sum(if(activity_type='start',-timestamp,timestamp))
    /count(distinct(process_id )) ,3) as processing_time 
from Activity
group by machine_id

5. 577.员工奖金

查找每个奖金少于1000的员工姓名和奖金数额
不存在员工只出现在 Bonus 表中的情况,且Bonus 有空,所以左连接Employee表作主表

	select *
	from Employee e left join Bonus b
	on e.empId = b.empId 

在这里插入图片描述

解法一、is null和<处理过滤条件

除了判断奖金<1000的员工,还隐藏没有奖金的;
所以用is null 筛选处理null值,

	select name ,bonus  
	from Employee e left join Bonus b
	on e.empId = b.empId 
	where b.bonus < 1000 or bonus is null 

解法二、if null空值转为0

	select name ,bonus  
	from Employee e left join Bonus b
	on e.empId = b.empId 
	where ifnull(bonus,0)<1000

6. 1280. 学生们参加各科测试的次数

查询每个学生参加每一门科目测试的次数

解法一、子查询

  1. 这里的考试表是学生id与考试科目的数量表,存在同名学生多次参加同一门学科、或者一次也没参加该学科的现象;以学生和科目进行分组,统计每名学生参加所选科目的次数,得到一个学生参加科目次数的表;
	select student_id,subject_name,count(1)attended_exams
	from Examinations
	group by student_id,subject_name

在这里插入图片描述

  1. 这里是查询每一门科目测试的次数,就算没参见显示0也要列出;所以应该是学生、科目的一次交叉连接,或者叫笛卡尔积,获取学生与所有科目的一个全表;
	select *
	from Students cross join Subjects

在这里插入图片描述

  1. 最后,这里肯定是左连接学生科目的全表,科目参加次数表中存在null的情况,即一次也没参加的科目,处理null值为0;最后还要按id和课程进行排序

在这里插入图片描述

	select a.student_id,a.student_name,b.subject_name,ifnull(c.attended_exams,0)attended_exams
	from Students a
	cross join Subjects b
	left join (
	    select student_id,subject_name,count(1)attended_exams
	    from Examinations
	    group by student_id,subject_name
	)c
	on a.student_id = c.student_id and b.subject_name = c.subject_name
	order by student_id,subject_name

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值